1+ use kas:: event:: NamedKey ;
2+ use kas:: prelude:: * ;
3+ use kas:: widgets:: { AccessLabel , Adapt , Button , EditBox } ;
14use std:: num:: ParseFloatError ;
25use std:: str:: FromStr ;
36
4- use kas:: event:: { Command , VirtualKeyCode as VK } ;
5- use kas:: prelude:: * ;
6- use kas:: widgets:: { EditBox , TextButton } ;
7+ type Key = kas:: event:: Key < kas:: event:: SmolStr > ;
78
8- #[ derive( Clone , Debug ) ]
9- enum Key {
10- Clear ,
11- DelBack ,
12- Divide ,
13- Multiply ,
14- Subtract ,
15- Add ,
16- Equals ,
17- Char ( char ) ,
9+ fn key_button ( label : & str ) -> Button < AccessLabel > {
10+ let string = AccessString :: from ( label) ;
11+ let key = string. key ( ) . unwrap ( ) . clone ( ) ;
12+ Button :: label_msg ( string, key)
1813}
19-
20- impl_scope ! {
21- // Buttons get keyboard bindings through the "&" item (e.g. "&1"
22- // binds both main and numpad 1 key) and via `with_keys`.
23- #[ widget{
24- layout = grid: {
25- 0 , 0 : TextButton :: new_msg( "&clear" , Key :: Clear ) . with_keys( & [ VK :: Delete ] ) ;
26- 1 , 0 : TextButton :: new_msg( "&÷" , Key :: Divide ) . with_keys( & [ VK :: Slash ] ) ;
27- 2 , 0 : TextButton :: new_msg( "&×" , Key :: Multiply ) . with_keys( & [ VK :: Asterisk ] ) ;
28- 3 , 0 : TextButton :: new_msg( "&−" , Key :: Subtract ) ;
29- 0 , 1 : TextButton :: new_msg( "&7" , Key :: Char ( '7' ) ) ;
30- 1 , 1 : TextButton :: new_msg( "&8" , Key :: Char ( '8' ) ) ;
31- 2 , 1 : TextButton :: new_msg( "&9" , Key :: Char ( '9' ) ) ;
32- 3 , 1 ..3 : TextButton :: new_msg( "&+" , Key :: Add ) ;
33- 0 , 2 : TextButton :: new_msg( "&4" , Key :: Char ( '4' ) ) ;
34- 1 , 2 : TextButton :: new_msg( "&5" , Key :: Char ( '5' ) ) ;
35- 2 , 2 : TextButton :: new_msg( "&6" , Key :: Char ( '6' ) ) ;
36- 0 , 3 : TextButton :: new_msg( "&1" , Key :: Char ( '1' ) ) ;
37- 1 , 3 : TextButton :: new_msg( "&2" , Key :: Char ( '2' ) ) ;
38- 2 , 3 : TextButton :: new_msg( "&3" , Key :: Char ( '3' ) ) ;
39- 3 , 3 ..5 : TextButton :: new_msg( "&=" , Key :: Equals )
40- . with_keys( & [ VK :: Return , VK :: NumpadEnter ] ) ;
41- 0 ..2 , 4 : TextButton :: new_msg( "&0" , Key :: Char ( '0' ) ) ;
42- 2 , 4 : TextButton :: new_msg( "&." , Key :: Char ( '.' ) ) ;
43- } ;
44- } ]
45- #[ derive( Debug , Default ) ]
46- struct Buttons ( widget_core!( ) ) ;
14+ fn key_button_with ( label : & str , key : Key ) -> Button < AccessLabel > {
15+ Button :: label_msg ( label, key. clone ( ) ) . with_access_key ( key)
4716}
4817
49- impl_scope ! {
50- #[ impl_default]
51- #[ widget{
52- layout = column: [
53- self . display,
54- Buttons :: default ( ) ,
55- ] ;
56- } ]
57- #[ derive( Debug ) ]
58- struct CalcUI {
59- core: widget_core!( ) ,
60- #[ widget] display: EditBox = EditBox :: new( "0" )
61- . with_editable( false )
62- . with_multi_line( true )
63- . with_lines( 3 , 3 )
64- . with_width_em( 5.0 , 10.0 ) ,
65- calc: Calculator = Calculator :: new( ) ,
66- }
67- impl Widget for Self {
68- fn configure( & mut self , mgr: & mut ConfigMgr ) {
69- mgr. disable_nav_focus( true ) ;
70-
71- // Enable key bindings without Alt held:
72- mgr. enable_alt_bypass( self . id_ref( ) , true ) ;
73-
74- mgr. register_nav_fallback( self . id( ) ) ;
75- }
76-
77- fn handle_event( & mut self , mgr: & mut EventMgr , event: Event ) -> Response {
78- match event {
79- Event :: Command ( Command :: DelBack ) => {
80- if self . calc. handle( Key :: DelBack ) {
81- * mgr |= self . display. set_string( self . calc. display( ) ) ;
82- }
83- Response :: Used
84- }
85- _ => Response :: Unused ,
86- }
87- }
88-
89- fn handle_message( & mut self , mgr: & mut EventMgr ) {
90- if let Some ( msg) = mgr. try_pop:: <Key >( ) {
91- if self . calc. handle( msg) {
92- * mgr |= self . display. set_string( self . calc. display( ) ) ;
93- }
94- }
95- }
96- }
97- impl Window for Self {
98- fn title( & self ) -> & str { "Calculator" }
18+ fn calc_ui ( ) -> impl Widget < Data = ( ) > {
19+ // We could use kas::widget::Text, but EditBox looks better.
20+ let display = EditBox :: string ( |calc : & Calculator | calc. display ( ) )
21+ . with_multi_line ( true )
22+ . with_lines ( 3 , 3 )
23+ . with_width_em ( 5.0 , 10.0 ) ;
24+
25+ // We use map_any to avoid passing input data (not wanted by buttons):
26+ let buttons = kas:: grid! {
27+ // Key bindings: C, Del
28+ ( 0 , 0 ) => Button :: label_msg( "&clear" , Key :: Named ( NamedKey :: Clear ) )
29+ . with_access_key( NamedKey :: Delete . into( ) ) ,
30+ // Widget is hidden but has key binding.
31+ // TODO(opt): exclude from layout & drawing.
32+ ( 0 , 0 ) => key_button_with( "" , NamedKey :: Backspace . into( ) ) ,
33+ ( 1 , 0 ) => key_button_with( "&÷" , Key :: Character ( "/" . into( ) ) ) ,
34+ ( 2 , 0 ) => key_button_with( "&×" , Key :: Character ( "*" . into( ) ) ) ,
35+ ( 3 , 0 ) => key_button_with( "&−" , Key :: Character ( "-" . into( ) ) ) ,
36+ ( 0 , 1 ) => key_button( "&7" ) ,
37+ ( 1 , 1 ) => key_button( "&8" ) ,
38+ ( 2 , 1 ) => key_button( "&9" ) ,
39+ ( 3 , 1 ..3 ) => key_button( "&+" ) ,
40+ ( 0 , 2 ) => key_button( "&4" ) ,
41+ ( 1 , 2 ) => key_button( "&5" ) ,
42+ ( 2 , 2 ) => key_button( "&6" ) ,
43+ ( 0 , 3 ) => key_button( "&1" ) ,
44+ ( 1 , 3 ) => key_button( "&2" ) ,
45+ ( 2 , 3 ) => key_button( "&3" ) ,
46+ ( 3 , 3 ..5 ) => key_button_with( "&=" , NamedKey :: Enter . into( ) ) ,
47+ ( 0 ..2 , 4 ) => key_button( "&0" ) ,
48+ ( 2 , 4 ) => key_button( "&." ) ,
9949 }
50+ . map_any ( ) ;
51+
52+ Adapt :: new ( kas:: column![ display, buttons] , Calculator :: new ( ) )
53+ . on_message ( |_, calc, key| calc. handle ( key) )
54+ . on_configure ( |cx, _| {
55+ cx. disable_nav_focus ( true ) ;
56+ cx. enable_alt_bypass ( true ) ;
57+ } )
10058}
10159
102- fn main ( ) -> kas:: shell :: Result < ( ) > {
60+ fn main ( ) -> kas:: app :: Result < ( ) > {
10361 env_logger:: init ( ) ;
10462
105- let theme = kas:: theme:: SimpleTheme :: new ( ) . with_font_size ( 16.0 ) ;
106- kas:: shell:: DefaultShell :: new ( theme) ?
107- . with ( CalcUI :: default ( ) ) ?
63+ let theme = kas_wgpu:: ShadedTheme :: new ( ) . with_font_size ( 16.0 ) ;
64+ kas:: app:: Default :: with_theme ( theme)
65+ . build ( ( ) ) ?
66+ . with ( Window :: new ( calc_ui ( ) , "Calculator" ) )
10867 . run ( )
10968}
11069
@@ -136,7 +95,7 @@ impl Calculator {
13695 fn state_str ( & self ) -> String {
13796 match & self . state {
13897 Ok ( x) => x. to_string ( ) ,
139- Err ( e) => format ! ( "{}" , e ) ,
98+ Err ( e) => format ! ( "{e}" ) ,
14099 }
141100 }
142101
@@ -151,32 +110,35 @@ impl Calculator {
151110 format ! ( "{}\n {}\n {}" , self . state_str( ) , op, & self . line_buf)
152111 }
153112
154- // return true if display changes
155- fn handle ( & mut self , key : Key ) -> bool {
113+ fn handle ( & mut self , key : Key ) {
156114 match key {
157- Key :: Clear => {
115+ Key :: Named ( NamedKey :: Clear ) | Key :: Named ( NamedKey :: Delete ) => {
158116 self . state = Ok ( 0.0 ) ;
159117 self . op = Op :: None ;
160118 self . line_buf . clear ( ) ;
161- true
162119 }
163- Key :: DelBack => self . line_buf . pop ( ) . is_some ( ) ,
164- Key :: Divide => self . do_op ( Op :: Divide ) ,
165- Key :: Multiply => self . do_op ( Op :: Multiply ) ,
166- Key :: Subtract => self . do_op ( Op :: Subtract ) ,
167- Key :: Add => self . do_op ( Op :: Add ) ,
168- Key :: Equals => self . do_op ( Op :: None ) ,
169- Key :: Char ( c) => {
170- self . line_buf . push ( c) ;
171- true
120+ Key :: Named ( NamedKey :: Backspace ) => {
121+ self . line_buf . pop ( ) ;
122+ }
123+ Key :: Character ( s) if s == "/" => self . do_op ( Op :: Divide ) ,
124+ Key :: Character ( s) if s == "*" => self . do_op ( Op :: Multiply ) ,
125+ Key :: Character ( s) if s == "-" => self . do_op ( Op :: Subtract ) ,
126+ Key :: Character ( s) if s == "+" => self . do_op ( Op :: Add ) ,
127+ Key :: Named ( NamedKey :: Enter ) => self . do_op ( Op :: None ) ,
128+ Key :: Character ( s) if s. len ( ) == 1 => {
129+ let c = s. chars ( ) . next ( ) . unwrap ( ) ;
130+ if ( '0' ..='9' ) . contains ( & c) || c == '.' {
131+ self . line_buf . push ( c) ;
132+ }
172133 }
134+ _ => ( ) ,
173135 }
174136 }
175137
176- fn do_op ( & mut self , next_op : Op ) -> bool {
138+ fn do_op ( & mut self , next_op : Op ) {
177139 if self . line_buf . is_empty ( ) {
178140 self . op = next_op;
179- return true ;
141+ return ;
180142 }
181143
182144 let line = f64:: from_str ( & self . line_buf ) ;
@@ -200,6 +162,5 @@ impl Calculator {
200162 }
201163
202164 self . op = next_op;
203- true
204165 }
205166}
0 commit comments