2929
3030mod wgpu_context;
3131
32+ use pollster:: FutureExt ;
3233use ratatui:: layout:: { Position , Rect } ;
3334use ratatui:: style:: { Color , Style } ;
3435use ratatui:: widgets:: StatefulWidget ;
3536
3637use crate :: wgpu_context:: WgpuContext ;
3738
39+ /// `ShaderCanvas` is a unit struct which implements the `StatefulWidget` trait from Ratatui.
40+ /// It holds the logic for applying the result of GPU computation to the `Buffer` struct which
41+ /// Ratatui uses to display to the terminal.
42+ ///
43+ /// ```rust,no_run
44+ /// let mut terminal = ratatui::init();
45+ /// let mut state = tui_shader::ShaderCanvasState::default();
46+ /// terminal.draw(|frame| {
47+ /// frame.render_stateful_widget(tui_shader::ShaderCanvas, frame.area(), &mut state);
48+ /// }).unwrap();
49+ /// ratatui::restore();
50+ /// ```
3851pub struct ShaderCanvas ;
3952
4053impl StatefulWidget for ShaderCanvas {
@@ -43,22 +56,23 @@ impl StatefulWidget for ShaderCanvas {
4356 let width = area. width ;
4457 let height = area. height ;
4558
46- let raw_buffer = state. wgpu_context . execute ( width, height) ;
59+ let raw_buffer = state. wgpu_context . execute ( width, height) . block_on ( ) ;
4760
4861 for y in 0 ..height {
4962 for x in 0 ..width {
5063 let index = ( y * ( width + WgpuContext :: row_padding ( width) ) + x) as usize ;
51- let pixel = raw_buffer[ index] ;
64+ let value = raw_buffer[ index] ;
65+ let position = ( x, y) ;
5266 let character = match state. options . character_rule {
5367 CharacterRule :: Always ( character) => character,
54- CharacterRule :: Map ( map) => map ( pixel . into ( ) ) ,
68+ CharacterRule :: Map ( map) => map ( Sample :: new ( value , position ) ) ,
5569 } ;
56- let color = Color :: Rgb ( pixel [ 0 ] , pixel [ 1 ] , pixel [ 2 ] ) ;
70+ let color = Color :: Rgb ( value [ 0 ] , value [ 1 ] , value [ 2 ] ) ;
5771 let style = match state. options . style_rule {
5872 StyleRule :: ColorFg => Style :: new ( ) . fg ( color) ,
5973 StyleRule :: ColorBg => Style :: new ( ) . bg ( color) ,
6074 StyleRule :: ColorFgAndBg => Style :: new ( ) . fg ( color) . bg ( color) ,
61- StyleRule :: Map ( map) => map ( pixel . into ( ) ) ,
75+ StyleRule :: Map ( map) => map ( Sample :: new ( value , position ) ) ,
6276 } ;
6377 let cell = buf. cell_mut ( Position :: new ( x, y) ) . unwrap ( ) ;
6478 cell. set_style ( style) ;
@@ -68,25 +82,45 @@ impl StatefulWidget for ShaderCanvas {
6882 }
6983}
7084
85+ /// State struct for [`ShaderCanvas`].
86+ ///
87+ /// This struct holds values that may want to be altered at runtime.
7188#[ derive( Debug , Default , Clone , Eq , PartialEq ) ]
7289pub struct ShaderCanvasState {
7390 wgpu_context : WgpuContext ,
7491 options : ShaderCanvasOptions ,
7592}
7693
7794impl ShaderCanvasState {
95+ /// Creates a new [`ShaderCanvasState`] by passing in the path to the desired fragment shader.
96+ /// The shader must be written in WGSL. The [`ShaderCanvasOptions`] will be set to
97+ /// [`Self::default()`].
7898 pub fn new ( path_to_fragment_shader : & str ) -> Self {
7999 Self :: new_with_options ( path_to_fragment_shader, ShaderCanvasOptions :: default ( ) )
80100 }
81101
102+ /// Creates a new [`ShaderCanvasState`] by passing in the path to the desired fragment shader.
103+ /// The shader must be written in WGSL. The [`ShaderCanvasOptions`] can be customized.
104+ ///
105+ /// ```rust,no_run
106+ /// let state = tui_shader::ShaderCanvasState::new_with_options("path/to/shader.wgsl",
107+ /// tui_shader::ShaderCanvasOptions {
108+ /// style_rule: tui_shader::StyleRule::ColorFg,
109+ /// entry_point: String::from("fragment"),
110+ /// ..Default::default()
111+ /// }
112+ /// );
113+ /// ```
82114 pub fn new_with_options ( path_to_fragment_shader : & str , options : ShaderCanvasOptions ) -> Self {
83115 Self {
84- wgpu_context : WgpuContext :: new ( path_to_fragment_shader, & options. entry_point ) ,
116+ wgpu_context : WgpuContext :: new ( path_to_fragment_shader, & options. entry_point )
117+ . block_on ( ) ,
85118 options,
86119 }
87120 }
88121}
89122
123+ /// Contains options to customize the behaviour of the ShaderCanvas.
90124#[ derive( Debug , Clone , Eq , PartialEq ) ]
91125pub struct ShaderCanvasOptions {
92126 pub character_rule : CharacterRule ,
@@ -104,6 +138,11 @@ impl Default for ShaderCanvasOptions {
104138 }
105139}
106140
141+ /// Determines which character to use for Cell.
142+ /// [`CharacterRule::Always`] takes a single char and applies it to all Cells in the [`ShaderCanvas`].
143+ /// [`CharacterRule::Map`] takes a function as an argument and allows you to map the input [`Sample`] to
144+ /// a character. For example, one might use the transparency value from the shader ([Sample::a]) and map
145+ /// it to a different character depending on the value.
107146#[ derive( Debug , Clone , Eq , PartialEq ) ]
108147pub enum CharacterRule {
109148 Always ( char ) ,
@@ -127,9 +166,14 @@ pub enum StyleRule {
127166
128167pub struct Sample {
129168 value : [ u8 ; 4 ] ,
169+ position : ( u16 , u16 ) ,
130170}
131171
132172impl Sample {
173+ fn new ( value : [ u8 ; 4 ] , position : ( u16 , u16 ) ) -> Self {
174+ Self { value, position }
175+ }
176+
133177 pub fn r ( & self ) -> u8 {
134178 self . value [ 0 ]
135179 }
@@ -145,11 +189,13 @@ impl Sample {
145189 pub fn a ( & self ) -> u8 {
146190 self . value [ 3 ]
147191 }
148- }
149192
150- impl From < [ u8 ; 4 ] > for Sample {
151- fn from ( value : [ u8 ; 4 ] ) -> Self {
152- Self { value }
193+ pub fn x ( & self ) -> u16 {
194+ self . position . 0
195+ }
196+
197+ pub fn y ( & self ) -> u16 {
198+ self . position . 1
153199 }
154200}
155201
@@ -160,7 +206,7 @@ mod tests {
160206 #[ test]
161207 fn default_wgsl_context ( ) {
162208 let mut context = WgpuContext :: default ( ) ;
163- let raw_buffer = context. execute ( 64 , 64 ) ;
209+ let raw_buffer = context. execute ( 64 , 64 ) . block_on ( ) ;
164210 assert ! ( raw_buffer. iter( ) . all( |pixel| pixel == & [ 255 , 0 , 255 , 255 ] ) ) ;
165211 }
166212}
0 commit comments