root/modules/kin/xtk/glass_button.kin

Revision 611:3dfd3a42adf7, 11.5 KB (checked in by Pete Kirkham <pete@…>, 2 years ago)

fixing issues with xtk and gfx libraries

Line 
1module kin::xtk::glass_button
2    import kin::io.stdout
3   
4    import kin::gfx.rgba
5    import kin::gfx.rect
6    import kin::gfx.linear_gradient
7    import kin::gfx.radial_gradient
8
9    actor seq_stream ( list )
10        def unary<- ( self )
11            match self.list
12                h :: t =>
13                    self become ( list = t )
14                    => h
15                _ =>
16                    fail #`element
17
18    actor button_model ( text )
19        let state = `nominal
20        let on_clicked = kin::xtk.event_delegate()
21       
22        def hover ( self )
23            match self.state
24                `hover => false
25                `pressed => false
26                _ =>
27                    self become ( state = `hover )
28                    => true
29               
30        def press ( self )
31            match self.state
32                `pressed => false
33                _ =>
34                    self become ( state = `pressed )
35                    => true
36               
37        def release ( self )
38            match self.state
39                `hover => false
40                `pressed =>
41                    self become ( state = `hover )
42                    self.on_clicked.propagate ( self )
43                _ =>
44                    self become ( state = `hover )
45            => true
46               
47        def reset ( self )
48            match self.state
49                `hover =>
50                    self become ( state = `nominal )
51                    => true
52                _ => false
53       
54        def set_text ( self, text )
55            self become ( text = text )
56            => true
57
58    actor button_presenter ( model )
59        let width  = 100.0
60        let height = 27.0
61        let x = 0.0
62        let y = 0.0
63       
64        def paint ( self, window, gfx ) => self.draw(gfx)
65       
66        def draw ( self, gfx )
67            let initial_trans = gfx.transform
68           
69            gfx.set_transform ( initial_trans.translate ( self.x, self.y ) )
70           
71            draw_button ( gfx, self.width, self.height, self.model )
72           
73            gfx.set_transform ( initial_trans )
74           
75        def desired_width ( self, gfx, mode )
76            let text_width = gfx.text_bounds ( font, self.model.text ).width
77           
78            => text_width + 8.0
79       
80        def desired_height ( self, gfx, mode )
81            => 28.0
82       
83        def set_bounds ( self, x:real64, y:real64, width:real64, height:real64 )
84            => self become ( x = x, y = y, width = width, height = height )
85           
86        def handle_event ( self, event )
87            match ( self.x <= event.x ) & ( self.x + self.width >= event.x ) & ( self.y <= event.y ) & ( self.y + self.height >= event.y )
88                false => self.model.reset()
89           
90            match event
91                _ : kin::xtk.button_press_event => self.model.press()
92                _ : kin::xtk.button_release_event => self.model.release()
93                _ => self.model.hover()
94
95    let pure_background = rgba ( 0xffffffff )
96    let background = pure_background.scale ( 0x7f )
97    let black = rgba ( 0x000000ff )
98    let white = rgba ( 0xffffffff )
99    let font = kin::gfx.load_font ( "FreeSans").with_height(12.0)
100    #~ let font = kin::gfx.load_font ( "Inconsolata" ).with_height(14).with_hinting(true).with_native_rendering(true)
101    let font_no_hint = font.with_hinting(false)
102   
103    def draw_button ( gfx, width, height, button )
104        let middle = height * 0.5
105        let initial_transform = gfx.transform
106       
107        match button.state
108            `pressed =>
109                gfx.set_transform ( gfx.transform.translate ( 0.0, 1.0 ) )
110
111        # main gradient - actually only one stripe
112        gfx.fill_rect ( background.scale ( 0x9a ), 2.0, 0.0, width - 4.0, middle )
113        gfx.fill_rect ( background.scale ( 0x6f ), 2.0, middle, width - 4.0, 1.0 )
114        gfx.fill_rect ( background.scale ( 0x45 ), 2.0, middle + 1.0, width - 4.0, height - middle - 4.0 )
115
116        # outline of button
117        let outline = background.scale ( 0x42 )
118
119        gfx.fill_rect ( outline, 2.0, 0.0, width - 4.0, 1.0 )
120        gfx.fill_rect ( outline, 2.0, height - 2.0, width - 4.0, 1.0 )
121        gfx.fill_rect ( outline, 0.0, 2.0, 1.0, height - 5.0 )
122        gfx.fill_rect ( outline, width - 1.0, 2.0, 1.0, height - 5.0 )
123        gfx.fill_rect ( outline, 1.0, 1.0, 1.0, 1.0 )
124        gfx.fill_rect ( outline, width - 2.0, 1.0, 1.0, 1.0 )
125
126        # inner border
127        let inner = background.scale ( 0x40 )
128       
129        let g1 = linear_gradient ( 1.0, 1.0, 1.0, middle, [ background, inner ], [ 0.0, 1.0 ] )
130       
131        gfx.fill_rect ( g1, 2.0, 1.0, width - 4.0, 1.0 )
132        gfx.fill_rect ( g1, 1.0, 2.0, 1.0, middle - 2.0 )
133        gfx.fill_rect ( g1, width - 2.0, 2.0, 1.0, middle - 2.0 )
134        gfx.fill_rect ( inner, 1.0, middle, 1.0, middle - 2.0 )
135        gfx.fill_rect ( inner, width - 2.0, middle, 1.0, middle - 2.0 )
136       
137        let g3 = radial_gradient ( 0.5 * width, height + 60.0, 100.0, [ background, inner ], [ 0.0, 1.0 ] )
138       
139        gfx.fill_rect ( g3, 2.0, height - 3.0, width - 4.0, 1.000 )
140
141        # bottom shading
142        let radius = width + height
143       
144        match button.state
145            `hover =>
146                let g3 = radial_gradient ( 0.5 * width, radius, radius, [ rgba ( 0xffffff7f ), rgba ( 0xffffff1f ) ], [ 0.0, 1.0 ] )
147                gfx.fill_rect ( g3, 2.0, 2.0, width - 4.0, height - 5.0 )
148            `pressed =>
149                let g3 = radial_gradient ( 0.5 * width, radius, radius, [ rgba ( 0xffcf00ff ), rgba ( 0xffcf000f ) ], [ 0.0, 1.0 ] )
150                gfx.fill_rect ( g3, 2.0, 2.0, width - 4.0, height - 5.0 )
151
152        # text
153        let text_width = gfx.text_bounds ( font, button.text ).width
154       
155        gfx.set_transform ( gfx.transform.translate ( ( width - text_width ) * 0.5, 18.0 ) )
156       
157        gfx.draw_text ( font, white, button.text, 0.0, 0.0 )
158       
159        gfx.set_transform ( initial_transform )
160
161    def draw_buttons ( out, gfx, buttons )
162        gfx.clear ( rgba ( 0x0000007f ) )
163       
164        fold ( ( x, btn ) => btn.set_bounds ( x, 0.0, btn.desired_width ( gfx, `normal ), btn.desired_height ( gfx, `normal ) ).x + btn.width + 4.0, 0.0, buttons )
165       
166        #~ gfx.clear ( rgba ( 0xffffffff ) )
167        gfx.fill_rect ( rgba ( 0x00001fff ), 0.0, 0.0, real64 ( gfx.width ), 28.0 )
168       
169        foreach ( ( b ) => b.draw(gfx), buttons )
170       
171        let text = "Can you tell the difference with or without hinting?"
172       
173        gfx.draw_text ( font, white, text ++ " - with ", 60.0, 60.0 )
174   
175        gfx.draw_text ( font_no_hint, white, text ++ " - without", 60.0, 80.0 )
176       
177        gfx.set_transform ( gfx.transform.translate(0,300).scale( 4, 4 ).rotate ( 0.2 ) )
178       
179        #~ gfx.set_transform ( gfx.transform.scale( 4, 4 ).translate ( 30, 30 ) )
180               
181        map ( ( b ) => b.draw(gfx), buttons )
182       
183        gfx.set_transform ( kin::gfx.identity.rotate ( -0.7 ).translate ( 300.0, 30.0 ) )
184               
185        draw_float_box ( gfx, 120, 30, rgba ( 0x8080ffff ) )
186       
187        let triangle = kin::gfx.path().move_to ( 120, 0 ).line_to ( 105, 15 ).line_to( 120, 30 ).close()
188       
189        gfx.fill ( white, triangle )
190       
191        gfx.draw_text ( font, white, "random tag", 6.0, 20.0 )
192       
193        gfx.set_transform ( kin::gfx.identity.translate ( 300, 200 ) )
194       
195        let or_gate = kin::gfx.path().move_to (0,0).quadratic_to(70,0,100,50).quadratic_to(70,100,0,100).quadratic_to(25,50,0,0).close()
196               
197        let g3 = linear_gradient ( 0, 0, 0, 100, [ white, black, black, white ], [ 0.0, 0.4, 0.6, 1.0 ] )
198       
199        gfx.fill ( black, or_gate )
200        gfx.stroke ( 4, g3, or_gate )
201       
202        #~ out <- font <- endl
203        #~ let text_bounds = gfx.text_bounds ( font, button.text )
204        #~ out <- text_bounds <- endl
205       
206    let shadow_colours = [ rgba ( 0x0000007f ), rgba ( 0x00000000 ) ]
207    let shadow_values  = [ 0.0, 1.0 ]
208   
209    # not how it would really work - I'd like a 2.5D API which does the usual
210    # blur alpha drop shadow
211    def draw_float_box ( gfx, width:real64, height:real64, fill )
212        let g1 = linear_gradient ( 0.0, 0.0, 0.0, 10.0, shadow_colours, shadow_values )
213        let g2 = radial_gradient ( 0.0, 0.0, 10.0,      shadow_colours, shadow_values )
214        let g3 = linear_gradient ( 0.0, 0.0, 10.0, 0.0, shadow_colours, shadow_values )
215       
216        let initial_trans = gfx.transform
217        let trans = gfx.transform.inverse().translate ( 0, -4 ).inverse()
218       
219        gfx.set_transform ( trans )
220        gfx.fill_rect ( g2, 0.0, 0.0, -10, -10 )
221       
222        gfx.set_transform ( trans.scale ( 1.0, -1.0 ) )
223        gfx.fill_rect ( g1, 0.0, 0.0, width, 10 )
224
225        gfx.set_transform ( trans.translate ( 0.0, height ) )
226        gfx.fill_rect ( g1, 0.0, 0.0, width, 10 )
227        gfx.fill_rect ( g2, 0.0, 0.0, -10, 10 )
228
229        gfx.set_transform ( trans.scale ( -1.0, 1.0 ) )
230        gfx.fill_rect ( g3, 0.0, 0.0, 10, height )
231
232        gfx.set_transform ( trans.translate ( width, 0.0 ) )
233        gfx.fill_rect ( g3, 0.0, 0.0, 10, height )
234        gfx.fill_rect ( g2, 0.0, 0.0, 10, -10 )
235       
236        gfx.set_transform ( trans.translate ( width, height ) )
237        gfx.fill_rect ( g2, 0.0, 0.0, 10, 10 )
238       
239        gfx.set_transform ( trans )
240        gfx.fill_rect ( rgba ( 0x0000007f ), -0.2, -0.2, width + 0.4, height + 0.4 )
241
242        gfx.set_transform ( initial_trans )
243        gfx.fill_rect ( fill, 0, 0, width, height )
244       
245    def update_buttons ( out, window, buttons, event )
246        let refresh_required = fold ( ( refresh, button ) => update_button ( out, window, button, event ) | refresh, false, buttons )
247       
248        match refresh_required
249            true => window.refresh()
250       
251    def update_button ( out, window, button, event )
252        => button.handle_event(event)
253
254    let messages = seq_stream ( [ "no, really", "please, no", "please", "...", ". . .", "last chance!" ] )
255       
256    def do_press ( window, button )
257        window.retitle ( "Ouch!" )
258        button.set_text ( <-messages )
259        window.refresh()
260        #~ button.on_clicked.add ( ( x ) => window.dispose() )
261        #~ button.on_clicked.add ( ( x ) => kin::xtk.halt() )
262        #~ button.on_clicked.add ( ( x ) => kin::core.exit ( 0 ) )
263
264    def create_button ( label ) => button_presenter ( button_model ( label ) )
265
266    def main ( in, out )
267        let window = kin::xtk.create_window ( `normal, rect ( 0, 0, 600, 400 ) )
268        let button = button_model ( "do not press" )
269        let buttons = button_presenter ( button ) :: map ( (txt)=> button_presenter ( button_model ( txt ) ), [ "and", "the", "other", "ones" ] )
270       
271        window.retitle ( "glass button" )
272       
273        window.set_painter ( ( gfx ) => draw_buttons ( out, gfx, buttons ) )
274        window.resize( +600, +400 )
275       
276        let updater = ( event ) => update_buttons ( out, window, buttons, event )
277       
278        window.on_pointer_moved.add ( updater )
279        window.on_pointer_exited.add ( updater )
280        window.on_button_pressed.add ( updater )
281        window.on_button_released.add ( updater )
282       
283        window.on_key_pressed.add  ( ( event ) => out <- "PRESS:   " <- event.keysymbol <- " " <- event.keychar <- " " <- event.timestamp <- endl )
284        window.on_key_released.add ( ( event ) => out <- "RELEASE: " <- event.keysymbol <- " " <- event.keychar <- " " <- event.timestamp <- endl )
285       
286        window.show()
287       
288        button.on_clicked.add ( ( button ) => do_press ( window, button ) )
289               
290        kin::xtk.event_loop()
Note: See TracBrowser for help on using the browser.