1 import ui 2 import gx 3 4 const win_width = 780 5 const win_height = 395 6 const nr_cols = 4 7 const cell_height = 25 8 const cell_width = 100 9 const table_width = cell_width * nr_cols 10 11 struct User { 12 first_name string 13 last_name string 14 age int 15 country string 16 } 17 18 @[heap] 19 struct State { 20 mut: 21 first_name string 22 last_name string 23 age string 24 password string 25 pbar &ui.ProgressBar 26 users []User 27 window &ui.Window = unsafe { nil } 28 label &ui.Label 29 country &ui.Radio 30 txt_pos int 31 started bool 32 is_error bool 33 } 34 35 fn main() { 36 mut logo := 'v-logo' 37 $if android { 38 logo = 'img/logo.png' 39 } 40 mut app := &State{ 41 users: [ 42 User{ 43 first_name: 'Sam' 44 last_name: 'Johnson' 45 age: 29 46 country: 'United States' 47 }, 48 User{ 49 first_name: 'Kate' 50 last_name: 'Williams' 51 age: 26 52 country: 'Canada' 53 }, 54 ] 55 country: ui.radio( 56 width: 200 57 values: ['United States', 'Canada', 'United Kingdom', 'Australia'] 58 title: 'Country' 59 ) 60 pbar: ui.progressbar( 61 width: 170 62 max: 10 63 val: 2 64 // theme: "red" 65 ) 66 label: ui.label(id: 'counter', text: '2/10', text_font_name: 'fixed_bold_italic') 67 } 68 mut window := ui.window( 69 width: win_width 70 height: win_height 71 title: 'V UI Demo' 72 mode: .resizable 73 // bg_color: ui.color_solaris 74 // theme: 'red' 75 native_message: false 76 layout: ui.row( 77 margin_: .02 78 spacing: .02 79 widths: [ui.compact, ui.stretch] // 1.0 == .64 + .3 + .02 + 2 * .02 80 children: [ 81 ui.column( 82 spacing: 10 83 widths: ui.compact 84 heights: ui.compact 85 scrollview: true 86 children: [ 87 ui.textbox( 88 max_len: 20 89 width: 200 90 placeholder: 'First name' 91 text: &app.first_name 92 // is_focused: &app.started 93 is_error: &app.is_error 94 is_focused: true 95 ), 96 ui.textbox( 97 max_len: 50 98 width: 200 99 placeholder: 'Last name' 100 text: &app.last_name 101 is_error: &app.is_error 102 ), 103 ui.textbox( 104 max_len: 3 105 width: 200 106 placeholder: 'Age' 107 is_numeric: true 108 text: &app.age 109 is_error: &app.is_error 110 ), 111 ui.textbox( 112 width: 200 113 placeholder: 'Password' 114 is_password: true 115 max_len: 20 116 text: &app.password 117 ), 118 ui.checkbox( 119 checked: true 120 text: 'Online registration' 121 ), 122 ui.checkbox(text: 'Subscribe to the newsletter'), 123 app.country, 124 ui.row( 125 id: 'btn_row' 126 widths: ui.compact 127 heights: 20.0 128 spacing: 80 129 children: [ 130 ui.button( 131 width: 60 132 text: 'Add user' 133 tooltip: 'Required fields:\n * First name\n * Last name\n * Age' 134 on_click: app.btn_add_click 135 radius: .0 136 ), 137 ui.button( 138 width: 40 139 tooltip: 'about' 140 text: '?' 141 on_click: btn_help_click 142 radius: .3 143 ), 144 ] 145 ), 146 ui.row( 147 spacing: 10 148 widths: [ 149 150.0, 150 40, 151 ] 152 heights: ui.compact 153 children: [ 154 app.pbar, 155 app.label, 156 ] 157 ), 158 ] 159 ), 160 ui.column( 161 scrollview: true 162 alignments: ui.HorizontalAlignments{ 163 center: [ 164 0, 165 ] 166 right: [ 167 1, 168 ] 169 } 170 widths: [ 171 ui.stretch, 172 ui.compact, 173 ] 174 heights: [ 175 ui.stretch, 176 ui.compact, 177 ] 178 children: [ 179 ui.canvas_plus( 180 width: 400 181 height: 275 182 on_draw: app.draw 183 bg_color: gx.Color{255, 220, 220, 150} 184 bg_radius: 10 185 // text_size: 20 186 ), 187 ui.picture( 188 id: 'logo' 189 width: 50 190 height: 50 191 path: logo 192 ), 193 ] 194 ), 195 ] 196 ) 197 ) 198 app.window = window 199 200 // add shortcut 201 window.add_shortcut_theme() 202 203 ui.run(window) 204 } 205 206 fn btn_help_click(b &ui.Button) { 207 // ui.message_box('Built with V UI') 208 b.ui.window.message(' Built with V UI\n Thus \n And') 209 } 210 211 /* 212 fn (mut app App) btn_add_click(b &Button) { 213 214 } 215 */ 216 fn (mut app State) btn_add_click(b &ui.Button) { 217 // println('nr users=$app.users.len') 218 // ui.notify('user', 'done') 219 // app.window.set_cursor(.hand) 220 if app.users.len >= 10 { 221 return 222 } 223 if app.first_name == '' || app.last_name == '' || app.age == '' { 224 app.is_error = true 225 return 226 } 227 new_user := User{ 228 first_name: app.first_name // first_name.text 229 last_name: app.last_name // .text 230 age: app.age.int() 231 country: app.country.selected_value() 232 } 233 app.users << new_user 234 app.pbar.val++ 235 app.first_name = '' 236 // app.first_name.focus() 237 app.last_name = '' 238 app.age = '' 239 app.password = '' 240 app.label.set_text('${app.users.len}/10') 241 // ui.message_box('$new_user.first_name $new_user.last_name has been added') 242 } 243 244 fn (app &State) draw(mut d ui.DrawDevice, c &ui.CanvasLayout) { 245 marginx, marginy := 20, 20 246 for i, user in app.users { 247 y := marginy + i * cell_height 248 // Outer border 249 c.draw_device_rect_empty(d, marginx, y, table_width, cell_height, gx.gray) 250 // Vertical separators 251 c.draw_device_line(d, cell_width, y, cell_width, y + cell_height, gx.gray) 252 c.draw_device_line(d, cell_width * 2, y, cell_width * 2, y + cell_height, gx.gray) 253 c.draw_device_line(d, cell_width * 3, y, cell_width * 3, y + cell_height, gx.gray) 254 // Text values 255 c.draw_device_text(d, marginx + 5, y + 5, user.first_name) 256 c.draw_device_text(d, marginx + 5 + cell_width, y + 5, user.last_name) 257 c.draw_device_text(d, marginx + 5 + cell_width * 2, y + 5, user.age.str()) 258 c.draw_device_text(d, marginx + 5 + cell_width * 3, y + 5, user.country) 259 } 260 }
1 import math 2 import os 3 import gx 4 import ui 5 6 @[heap] 7 struct App { 8 mut: 9 text string 10 window &ui.Window 11 rows []&ui.Layout 12 result f64 13 is_float bool 14 new_number bool 15 operands []f64 16 operations []string 17 } 18 19 fn main() { 20 ops := [ 21 ['C', '%', '^', '÷'], 22 ['7', '8', '9', '*'], 23 ['4', '5', '6', '-'], 24 ['1', '2', '3', '+'], 25 ['0', '.', '±', '='], 26 ] 27 mut app := &App{ 28 window: unsafe { nil } 29 } 30 mut children := []ui.Widget{} 31 children = [ 32 ui.textbox( 33 text: &app.text 34 placeholder: '0' 35 fitted_height: true 36 // width: 135 37 text_size: 1.0 / 10 38 read_only: true 39 ), 40 ] 41 42 for row_ops in ops { 43 mut row_children := []ui.Widget{} 44 for op in row_ops { 45 row_children << ui.button( 46 text: op 47 on_click: app.btn_click 48 text_size: 1.0 / 20 49 radius: .25 50 // theme: 'blue' 51 hoverable: true 52 ) 53 } 54 children << ui.row( 55 spacing: .02 56 widths: ui.stretch 57 children: row_children 58 ) 59 } 60 61 app.window = ui.window( 62 width: 300 63 height: 400 64 title: 'V Calc' 65 mode: .resizable // .max_size // 66 font_path: os.resource_abs_path(os.join_path('../assets/fonts/', 'RobotoMono-Regular.ttf')) 67 theme: 'red' 68 children: [ 69 ui.column( 70 margin_: 10 71 spacing: .02 72 heights: ui.stretch // [ui.compact, ui.stretch, ui.stretch, ui.stretch, ui.stretch, ui.stretch] // or [30.0, ui.stretch, ui.stretch, ui.stretch, ui.stretch, ui.stretch] 73 bg_color: gx.rgb(240, 180, 130) 74 children: children 75 ), 76 ] 77 ) 78 // app.text = "size= ${app.window.width} ${app.window.height}" 79 app.window.add_shortcut_theme() 80 ui.run(app.window) 81 } 82 83 fn (mut app App) btn_click(btn &ui.Button) { 84 op := btn.text 85 number := app.text 86 if op == 'C' { 87 app.result = 0 88 app.operands = [] 89 app.operations = [] 90 app.new_number = true 91 app.is_float = false 92 app.update_result() 93 return 94 } 95 if op[0].is_digit() || op == '.' { 96 // Can only have one `.` in a number 97 if op == '.' && number.contains('.') { 98 return 99 } 100 if app.new_number { 101 app.text = btn.text 102 app.new_number = false 103 app.is_float = false 104 } else { 105 // Append a new digit 106 app.text = number + btn.text 107 } 108 return 109 } 110 if number.contains('.') { 111 app.is_float = true 112 } 113 if op in ['+', '-', '÷', '*', '±', '='] { 114 if !app.new_number { 115 app.new_number = true 116 app.operands << number.f64() 117 } 118 app.operations << op 119 app.calculate() 120 } 121 app.update_result() 122 } 123 124 fn (mut app App) update_result() { 125 // Format and print the result 126 if !math.trunc(app.result).eq_epsilon(app.result) { 127 app.text = '${app.result:-15.10f}' 128 } else { 129 app.text = int(app.result).str() 130 } 131 } 132 133 fn pop_f64(a []f64) (f64, []f64) { 134 res := a.last() 135 return res, a[0..a.len - 1] 136 } 137 138 fn pop_string(a []string) (string, []string) { 139 res := a.last() 140 return res, a[0..a.len - 1] 141 } 142 143 fn (mut app App) calculate() { 144 mut a := f64(0) 145 mut b := f64(0) 146 mut op := '' 147 mut operands := app.operands.clone() 148 mut operations := app.operations.clone() 149 mut result := if operands.len == 0 { f64(0.0) } else { operands.last() } 150 mut i := 0 151 for { 152 i++ 153 if operations.len == 0 { 154 break 155 } 156 op, operations = pop_string(operations) 157 if op == '=' { 158 continue 159 } 160 if operands.len < 1 { 161 operations << op 162 break 163 } 164 b, operands = pop_f64(operands) 165 if op == '±' { 166 result = -b 167 operands << result 168 continue 169 } 170 if operands.len < 1 { 171 operations << op 172 operands << b 173 break 174 } 175 a, operands = pop_f64(operands) 176 match op { 177 '+' { 178 result = a + b 179 } 180 '-' { 181 result = a - b 182 } 183 '*' { 184 result = a * b 185 } 186 '÷' { 187 if int(b) == 0 { 188 eprintln('Division by zero!') 189 b = 0.0000000001 190 } 191 result = a / b 192 } 193 else { 194 operands << a 195 operands << b 196 result = b 197 eprintln('Unknown op: ${op} ') 198 break 199 } 200 } 201 operands << result 202 // eprintln('i: ${i:4d} | res: ${result} | op: $op | operands: $operands | operations: $operations') 203 } 204 app.operations = operations 205 app.operands = operands 206 app.result = result 207 // eprintln('----------------------------------------------------') 208 // eprintln('Operands: $app.operands | Operations: $app.operations ') 209 // eprintln('-------- result: $result | i: $i -------------------') 210 }
1 import ui 2 import gx 3 4 const win_width = 1200 5 const win_height = 500 6 const btn_width = 200 7 const btn_height = 30 8 const port = 1337 9 const lb_height = 0 10 11 @[heap] 12 struct App { 13 mut: 14 sizes map[string]f64 15 } 16 17 fn main() { 18 mut app := &App{} 19 app.sizes = { 20 '100': 100.0 21 '20': 20.0 22 '.3': .3 23 'ui.stretch': ui.stretch 24 '1.5*ui.stretch': 1.5 * ui.stretch 25 '2*ui.stretch': 2 * ui.stretch 26 '3*ui.stretch': 3 * ui.stretch 27 } 28 window := ui.window( 29 width: win_width 30 height: win_height 31 title: 'Stack widths and heights management' 32 mode: .resizable 33 on_resize: win_resize 34 on_init: win_init 35 layout: ui.column( 36 heights: [ui.compact, ui.compact, ui.stretch] 37 spacing: .01 38 children: [ 39 ui.row( 40 widths: ui.compact 41 heights: ui.compact 42 margin_: 5 43 spacing: .03 44 children: [ 45 ui.row( 46 id: 'row_btn1' 47 title: 'btn1' 48 margin_: .05 49 spacing: .1 50 widths: ui.compact 51 heights: ui.compact 52 children: [ 53 ui.listbox( 54 id: 'lb1w' 55 height: lb_height 56 selection: 0 57 on_change: app.lb_change 58 items: { 59 '.3': '.3' 60 '100': '100' 61 'ui.stretch': 'ui.stretch' 62 'ui.compact': 'ui.compact' 63 '1.5*ui.stretch': '1.5 * ui.stretch' 64 '2*ui.stretch': '2 * ui.stretch' 65 '3*ui.stretch': '3 * ui.stretch' 66 } 67 ), 68 ui.listbox( 69 id: 'lb1h' 70 height: lb_height 71 selection: 0 72 on_change: app.lb_change 73 items: { 74 '.3': '.3' 75 '20': '20' 76 'ui.stretch': 'ui.stretch' 77 'ui.compact': 'ui.compact' 78 } 79 ), 80 ] 81 ), 82 ui.row( 83 id: 'row_btn2' 84 title: 'btn2' 85 margin_: .05 86 spacing: .1 87 widths: ui.compact 88 heights: ui.compact 89 children: [ 90 ui.listbox( 91 id: 'lb2w' 92 height: lb_height 93 selection: 1 94 on_change: app.lb_change 95 items: { 96 '.3': '.3' 97 '100': '100' 98 'ui.stretch': 'ui.stretch' 99 'ui.compact': 'ui.compact' 100 '1.5*ui.stretch': '1.5 * ui.stretch' 101 '2*ui.stretch': '2 * ui.stretch' 102 '3*ui.stretch': '3 * ui.stretch' 103 } 104 ), 105 ui.listbox( 106 id: 'lb2h' 107 height: lb_height 108 selection: 1 109 on_change: app.lb_change 110 items: { 111 '.3': '.3' 112 '20': '20' 113 'ui.stretch': 'ui.stretch' 114 'ui.compact': 'ui.compact' 115 } 116 ), 117 ] 118 ), 119 ui.row( 120 id: 'row_space' 121 title: 'Margins and Spacing' 122 margin_: .05 123 spacing: .1 124 widths: ui.compact 125 heights: ui.compact 126 children: [ 127 ui.listbox( 128 id: 'lbmargin' 129 height: lb_height 130 selection: 3 131 on_change: lb_change_sp 132 items: { 133 '20': 'margin_: 20' 134 '50': 'margin_: 50' 135 '.05': 'margin_: .05' 136 '.1': 'margin_: .1' 137 } 138 ), 139 ui.listbox( 140 id: 'lbspace' 141 height: lb_height 142 selection: 3 143 on_change: lb_change_sp 144 items: { 145 '20': 'spacing: 20' 146 '50': 'spacing: 50' 147 '.05': 'spacing: .05' 148 '.1': 'spacing: .1' 149 } 150 ), 151 ] 152 ), 153 ] 154 ), 155 ui.column( 156 margin: ui.Margin{ 157 right: .05 158 left: .05 159 } 160 spacing: .01 161 widths: ui.stretch 162 bg_color: gx.Color{255, 255, 255, 128} 163 children: [ 164 ui.label( 165 id: 'l_btns_sizes' 166 height: 25 167 text: 'Button 1 & 2 declaration: ui.button(width: 200, height: 30, ...)' 168 ), 169 ui.label( 170 id: 'l_stack_sizes' 171 height: 25 172 text: 'Row (Stack) declaration: ui.row( margin_: 20, spacing: 20, widths: [.3, 100], heights: [.3, ui.compact])' 173 ), 174 ] 175 ), 176 ui.row( 177 id: 'row' 178 widths: [ 179 .3, 180 100, 181 ] 182 heights: [ 183 .3, 184 ui.compact, 185 ] 186 margin_: .1 187 spacing: .1 188 bg_color: gx.Color{50, 100, 0, 50} 189 children: [ 190 ui.button( 191 id: 'btn1' 192 width: 200 193 height: 30 194 text: 'Button 1' 195 ), 196 ui.button( 197 id: 'btn2' 198 width: 200 199 height: 30 200 text: 'Button 2' 201 ), 202 ] 203 ), 204 ] 205 ) 206 ) 207 ui.run(window) 208 } 209 210 fn (app &App) lb_change(lb &ui.ListBox) { 211 key, _ := lb.selected() or { '100', '' } 212 213 // mut sw, mut sh := lb.size() 214 // println('lb_change: ($sw, $sh)') 215 win := lb.ui.window 216 217 /* 218 row1 := win.stack("row_btn1") 219 sw, sh = row1.size() 220 print("row_btn1: ($sw, $sh) and ") 221 row2 := win.stack("row_btn2") 222 sw, sh = row2.size() 223 println("row_btn1: ($sw, $sh)")*/ 224 225 mut iw, mut ih := -1, -1 226 match lb.id { 227 'lb1w' { 228 iw = 0 229 } 230 'lb2w' { 231 iw = 1 232 } 233 'lb1h' { 234 ih = 0 235 } 236 'lb2h' { 237 ih = 1 238 } 239 else {} 240 } 241 242 mut s := win.get_or_panic[ui.Stack]('row') 243 // if mut s is ui.Stack { 244 if iw >= 0 { 245 if key == 'ui.compact' { 246 s.widths[iw] = f32(btn_width) 247 } else { 248 s.widths[iw] = f32(app.sizes[key]) 249 } 250 } 251 if ih >= 0 { 252 if key == 'ui.compact' { 253 s.heights[ih] = f32(btn_height) 254 } else { 255 s.heights[ih] = f32(app.sizes[key]) 256 } 257 } 258 set_output_label(win) 259 win.update_layout() 260 set_sizes_labels(win) 261 // } else { 262 // println('$s.type_name()') 263 // } 264 } 265 266 fn lb_change_sp(lb &ui.ListBox) { 267 key, _ := lb.selected() or { '10', '' } 268 269 win := lb.ui.window 270 mut s := win.get_or_panic[ui.Stack]('row') 271 272 match lb.id { 273 'lbspace' { 274 s.spacings[0] = key.f32() 275 } 276 'lbmargin' { 277 marg := key.f32() 278 s.margins.top, s.margins.bottom, s.margins.left, s.margins.right = marg, marg, marg, marg 279 } 280 else {} 281 } 282 283 set_output_label(win) 284 win.update_layout() 285 set_sizes_labels(win) 286 } 287 288 fn set_output_label(win &ui.Window) { 289 lb1w := win.get_or_panic[ui.ListBox]('lb1w') 290 lb1h := win.get_or_panic[ui.ListBox]('lb1h') 291 lb2w := win.get_or_panic[ui.ListBox]('lb2w') 292 lb2h := win.get_or_panic[ui.ListBox]('lb2h') 293 mut w1, mut w2, mut h1, mut h2 := '', '', '', '' 294 _, w1 = lb1w.selected() or { '100', '' } 295 _, w2 = lb2w.selected() or { '100', '' } 296 _, h1 = lb1h.selected() or { '100', '' } 297 _, h2 = lb2h.selected() or { '100', '' } 298 299 lbm := win.get_or_panic[ui.ListBox]('lbmargin') 300 lbs := win.get_or_panic[ui.ListBox]('lbspace') 301 _, marg := lbm.selected() or { '100', '' } 302 _, sp := lbs.selected() or { '100', '' } 303 mut lss := win.get_or_panic[ui.Label]('l_stack_sizes') 304 lss.set_text('Row (Stack) declaration: ui.row( ${marg}, ${sp}, widths: [${w1}, ${w2}], heights: [${h1}, ${h2}])') 305 } 306 307 fn set_sizes_labels(win &ui.Window) { 308 mut btn1 := win.get_or_panic[ui.Button]('btn1') 309 mut row_btn1 := win.get_or_panic[ui.Stack]('row_btn1') 310 mut w, mut h := btn1.size() 311 row_btn1.title = 'Btn1: (${w}, ${h})' 312 313 mut row_btn2 := win.get_or_panic[ui.Stack]('row_btn2') 314 mut btn2 := win.get_or_panic[ui.Button]('btn2') 315 w, h = btn2.size() 316 row_btn2.title = 'Btn2: (${w}, ${h})' 317 } 318 319 fn win_resize(win &ui.Window, w int, h int) { 320 set_sizes_labels(win) 321 } 322 323 fn win_init(win &ui.Window) { 324 set_sizes_labels(win) 325 mut lb := win.get_or_panic[ui.ListBox]('lb1w') 326 sw, sh := lb.size() 327 mut row := win.get_or_panic[ui.Stack]('row_btn1') 328 rw, rh := row.size() 329 println('win init (${sw}, ${sh}) (${row.x}, ${row.y} ,${rw}, ${rh})') 330 set_output_label(win) 331 win.update_layout() 332 }
1 import ui 2 import gx 3 4 const win_width = 550 5 const win_height = 385 6 7 fn main() { 8 mut text := 'gcghchc\n fvfyfy' + 'titi\n'.repeat(10) 9 mut window := ui.window( 10 width: win_width 11 height: win_height 12 title: 'V UI Demo' 13 mode: .resizable 14 on_init: win_init 15 layout: ui.canvas_layout( 16 id: 'demo_cl' 17 on_draw: draw 18 active_evt_mngr: false 19 on_mouse_move: mouse_move 20 full_width: win_width - 20 21 full_height: win_height 22 scrollview: true 23 children: [ 24 ui.at(10, 40, ui.row( 25 spacing: 10 26 heights: ui.compact 27 children: [ 28 ui.button(z_index: 10, text: 'X'), 29 ui.button(z_index: 10, text: 'Add'), 30 ] 31 )), 32 ui.at(10, 10, ui.button( 33 id: 'b_thm' 34 text: 'Theme' 35 width: 100 36 theme: 'red' 37 z_index: 10 38 movable: true 39 hoverable: true 40 on_click: fn (b &ui.Button) { 41 ui.message_box('Built with V UI') 42 } 43 )), 44 ui.at(20, 340, ui.label( 45 id: 'l_mm' 46 text: '(0, 0) ' 47 )), 48 ui.at(120, 10, ui.dropdown( 49 width: 140 50 height: 20 51 def_text: 'Select a theme' 52 on_selection_changed: dd_change 53 items: [ 54 ui.DropdownItem{ 55 text: 'default' 56 }, 57 ui.DropdownItem{ 58 text: 'blue' 59 }, 60 ui.DropdownItem{ 61 text: 'red' 62 }, 63 ] 64 )), 65 ui.at(10, 70, ui.listbox( 66 width: 100 67 height: 140 68 z_index: 10 69 on_change: lb_change 70 ordered: true 71 // scrollview: false 72 draw_lines: true 73 items: { 74 'default': 'Classic' 75 'blue': 'Blue' 76 'red': 'Red' 77 'classic2': 'Classic2' 78 'blue2': 'Blue2' 79 'red2': 'Red2' 80 'classic3': 'Classic3' 81 'blue3': 'Blue3' 82 'red3': 'Red3' 83 } 84 )), 85 ui.at(50, 220, ui.listbox( 86 width: 100 87 height: 100 88 z_index: 10 89 on_change: lb_change_multi 90 scrollview: false 91 // selectable: false 92 ordered: true 93 multi: true 94 draw_lines: true 95 items: { 96 'classic': 'Classic' 97 'blue': 'Blue' 98 'red': 'Red' 99 } 100 )), 101 ui.at(200, 220, ui.listbox( 102 width: 100 103 height: 100 104 z_index: 10 105 on_change: lb_change_multi 106 scrollview: false 107 // selectable: false 108 ordered: true 109 multi: true 110 draw_lines: true 111 bg_color: gx.red 112 items: { 113 'classic': 'Classic' 114 } 115 )), 116 ui.at(150, 100, ui.menu( 117 id: 'menu' 118 text: 'Menu' 119 width: 200 120 items: [ 121 ui.menuitem( 122 text: 'Delete all users' 123 action: menu_click 124 ), 125 ui.menuitem( 126 text: 'Export users' 127 action: menu_click 128 ), 129 ui.menuitem( 130 text: 'Exit' 131 action: menu_click 132 ), 133 ] 134 )), 135 ui.at(150, 80, ui.button( 136 text: 'hide/show menu' 137 z_index: 10 138 on_click: fn (b &ui.Button) { 139 mut menu := b.ui.window.get_or_panic[ui.Menu]('menu') 140 menu.hidden = !menu.hidden 141 } 142 )), 143 ui.at(300, 30, ui.textbox( 144 id: 'tb' 145 width: 150 146 height: 100 147 mode: .multiline 148 bg_color: gx.yellow 149 text: &text 150 )), 151 ] 152 ) 153 ) 154 ui.run(window) 155 } 156 157 fn menu_click(item &ui.MenuItem) { 158 println('menu here ${item.text}') 159 } 160 161 fn dd_change(dd &ui.Dropdown) { 162 println(dd.selected().text) 163 win := dd.ui.window 164 mut b := win.get_or_panic[ui.Button]('b_thm') 165 b.update_theme_style(dd.selected().text) 166 } 167 168 fn lb_change(lb &ui.ListBox) { 169 id, _ := lb.selected() or { 'classic', '' } 170 171 win := lb.ui.window 172 mut b := win.get_or_panic[ui.Button]('b_thm') 173 b.update_theme_style(id) 174 } 175 176 fn lb_change_multi(lb &ui.ListBox) { 177 println(lb.items.map('${it.text}: ${it.selected} ${it.disabled}')) 178 } 179 180 fn draw(mut d ui.DrawDevice, c &ui.CanvasLayout) { 181 w, h := c.full_width, c.full_height 182 c.draw_device_rect_filled(d, 0, 0, w, h, gx.white) 183 } 184 185 fn mouse_move(c &ui.CanvasLayout, e ui.MouseMoveEvent) { 186 mut l := c.ui.window.get_or_panic[ui.Label]('l_mm') 187 l.set_text('(${e.x},${e.y})') 188 } 189 190 fn win_init(mut w ui.Window) { 191 // w.mouse.start(ui.mouse_hidden) 192 }
1 import ui 2 import gx 3 4 const win_width = 400 5 const win_height = 300 6 7 struct App { 8 mut: 9 text string 10 btn_cb map[string]fn (&ui.Button) 11 } 12 13 fn make_tb(mut app App, has_row bool) ui.Widget { 14 tb := ui.textbox( 15 mode: .multiline 16 bg_color: gx.yellow 17 text: &app.text 18 ) 19 return if has_row { 20 ui.Widget(ui.row( 21 widths: ui.stretch 22 children: [ 23 tb, 24 ] 25 )) 26 } else { 27 ui.Widget(tb) 28 } 29 } 30 31 fn (mut app App) make_btn() ui.Widget { 32 app.btn_cb['btn_click'] = fn (_ &ui.Button) { 33 ui.message_box('coucou toto!') 34 } 35 return ui.button( 36 text: 'toto' 37 on_click: app.btn_cb['btn_click'] 38 ) 39 } 40 41 fn main() { 42 mut with_row := false 43 $if with_row ? { 44 with_row = true 45 } 46 mut app := App{ 47 text: 'blah blah blah\n'.repeat(10) 48 } 49 ui.run(ui.window( 50 width: win_width 51 height: win_height 52 title: 'V UI: Rectangles inside BoxLayout' 53 mode: .resizable 54 layout: ui.box_layout( 55 id: 'bl' 56 children: { 57 'id1: (0,0) ++ (30,30)': ui.rectangle( 58 color: gx.rgb(255, 100, 100) 59 ) 60 'id2: (30,30) -> (-30.5,-30.5)': ui.rectangle( 61 color: gx.rgb(100, 255, 100) 62 ) 63 'id3: (50%,50%) -> (100%,100%)': make_tb(mut app, with_row) 64 'id4: (-30.5, -30.5) ++ (30,30)': ui.rectangle( 65 color: gx.white 66 ) 67 'id5: (70%,20%) ++ (50,20)': app.make_btn() 68 } 69 ) 70 )) 71 }
1 import ui 2 import ui.component as uic 3 import gx 4 5 const win_width = 800 6 const win_height = 600 7 8 @[heap] 9 struct App { 10 mut: 11 window &ui.Window = unsafe { nil } 12 // group 13 first_ipsum string 14 second_ipsum string 15 full_name string 16 // slider 17 hor_slider &ui.Slider = unsafe { nil } 18 vert_slider &ui.Slider = unsafe { nil } 19 } 20 21 fn main() { 22 mut app := &App{} 23 app.hor_slider = ui.slider( 24 width: 200 25 height: 20 26 orientation: .horizontal 27 max: 100 28 val: 0 29 on_value_changed: app.on_hor_value_changed 30 ) 31 app.vert_slider = ui.slider( 32 width: 20 33 height: 200 34 orientation: .vertical 35 max: 100 36 val: 0 37 on_value_changed: app.on_vert_value_changed 38 ) 39 cr := ui.column( 40 id: 'col_radio' 41 widths: ui.stretch 42 margin_: 5 43 spacing: 10 44 children: [ 45 ui.row( 46 spacing: 5 47 children: [ 48 ui.label(text: 'Compact'), 49 ui.switcher(open: true, on_click: on_switch_click), 50 ] 51 ), 52 ui.radio( 53 id: 'rh1' 54 horizontal: true 55 compact: true 56 values: [ 57 'United States', 58 'Canada', 59 'United Kingdom', 60 'Australia', 61 ] 62 title: 'Country' 63 ), 64 ui.radio( 65 values: [ 66 'United States', 67 'Canada', 68 'United Kingdom', 69 'Australia', 70 ] 71 title: 'Country' 72 ), 73 ui.row( 74 widths: [ 75 ui.compact, 76 ui.stretch, 77 ] 78 children: [ 79 ui.label(text: 'Country:'), 80 ui.radio( 81 id: 'rh2' 82 horizontal: true 83 compact: true 84 values: ['United States', 'Canada', 'United Kingdom', 'Australia'] 85 ), 86 ] 87 ), 88 ] 89 ) 90 cdd := ui.column( 91 id: 'col_dd' 92 margin_: 5 93 widths: ui.compact 94 children: [ 95 ui.dropdown( 96 width: 140 97 def_text: 'Select an option' 98 on_selection_changed: dd_change 99 items: [ 100 ui.DropdownItem{ 101 text: 'Delete all users' 102 }, 103 ui.DropdownItem{ 104 text: 'Export users' 105 }, 106 ui.DropdownItem{ 107 text: 'Exit' 108 }, 109 ] 110 ), 111 ui.rectangle( 112 height: 100 113 width: 250 114 color: gx.rgb(100, 255, 100) 115 ), 116 ] 117 ) 118 rg := ui.row( 119 id: 'row_group' 120 margin_: 10 121 height: 200 122 spacing: 20 123 children: [ 124 ui.group( 125 title: 'First group' 126 children: [ 127 ui.textbox( 128 max_len: 20 129 width: 200 130 placeholder: 'Lorem ipsum' 131 text: &app.first_ipsum 132 ), 133 ui.textbox( 134 max_len: 20 135 width: 200 136 placeholder: 'dolor sit amet' 137 text: &app.second_ipsum 138 ), 139 ui.button( 140 text: 'More ipsum!' 141 on_click: fn (b &ui.Button) { 142 ui.open_url('https://lipsum.com/feed/html') 143 } 144 ), 145 ] 146 ), 147 ui.group( 148 title: 'Second group' 149 children: [ 150 ui.textbox( 151 max_len: 20 152 width: 200 153 placeholder: 'Full name' 154 text: &app.full_name 155 ), 156 ui.checkbox(checked: true, text: 'Do you like V?'), 157 ui.button(text: 'Submit'), 158 ] 159 ), 160 ] 161 ) 162 rs := ui.row( 163 id: 'row_slider' 164 height: 200 165 alignment: .center 166 widths: [.1, .9] 167 heights: [.9, .1] 168 margin: ui.Margin{25, 25, 25, 25} 169 spacing: 10 170 children: [app.vert_slider, app.hor_slider] 171 ) 172 rect := ui.rectangle( 173 text: 'Here a simple ui rectangle' 174 color: gx.red 175 height: 100 176 // text_color: gx.blue 177 // text_align: gx.align_left 178 // text_size: 30 179 ) 180 window := ui.window( 181 width: win_width 182 height: win_height 183 title: 'V UI: Accordion' 184 native_message: false 185 mode: .resizable 186 layout: uic.accordion_stack( 187 id: 'demo' 188 text_color: gx.blue 189 titles: ['Rectangle', 'Radio', 'Slider', 'Group', 'Dropdown'] 190 children: [rect, cr, rs, rg, cdd] 191 heights: [30.0, ui.compact] 192 scrollview: true 193 ) 194 ) 195 app.window = window 196 ui.run(window) 197 } 198 199 fn on_switch_click(switcher &ui.Switch) { 200 // switcher_state := if switcher.open { 'Enabled' } else { 'Disabled' } 201 // app.label.set_text(switcher_state) 202 mut rh1 := switcher.ui.window.get_or_panic[ui.Radio]('rh1') 203 rh1.compact = !rh1.compact 204 mut rh2 := switcher.ui.window.get_or_panic[ui.Radio]('rh2') 205 rh2.compact = !rh2.compact 206 switcher.ui.window.update_layout() 207 } 208 209 fn dd_change(dd &ui.Dropdown) { 210 println(dd.selected().text) 211 } 212 213 fn (mut app App) on_hor_value_changed(slider &ui.Slider) { 214 app.hor_slider.val = app.hor_slider.val 215 } 216 217 fn (mut app App) on_vert_value_changed(slider &ui.Slider) { 218 app.vert_slider.val = app.vert_slider.val 219 }
1 import ui 2 import ui.component as uic 3 import gx 4 5 const win_width = 30 + 256 + 4 * 10 + uic.cb_cv_hsv_w 6 const win_height = 376 7 8 fn main() { 9 cb_layout := uic.colorbox_stack(id: 'cbox', light: false, hsl: false) 10 rect := ui.rectangle(text: 'Here a simple ui rectangle') 11 mut dtw := ui.DrawTextWidget(rect) 12 dtw.update_style(color: gx.blue, size: 30) 13 window := ui.window( 14 width: win_width 15 height: win_height 16 title: 'V UI: Toolbar' 17 native_message: false 18 layout: ui.column( 19 heights: [ui.compact, ui.compact] 20 children: [cb_layout, rect] 21 ) 22 ) 23 mut cb := uic.colorbox_component(cb_layout) 24 cb.connect(&rect.style.color) 25 ui.run(window) 26 }
1 // Same as demo_component_filebrowser with folder_only: true 2 import ui 3 import ui.component as uic 4 5 const win_width = 800 6 const win_height = 600 7 8 fn main() { 9 window := ui.window( 10 width: win_width 11 height: win_height 12 title: 'V UI: File Browser' 13 native_message: false 14 mode: .resizable 15 layout: uic.filebrowser_stack( 16 id: 'fb' 17 on_click_ok: on_click_ok 18 on_click_cancel: on_click_cancel 19 folder_only: true 20 ) 21 ) 22 ui.run(window) 23 } 24 25 fn on_click_ok(b &ui.Button) { 26 println(uic.filebrowser_component(b).selected_full_title()) 27 } 28 29 fn on_click_cancel(b &ui.Button) { 30 if b.ui.dd is ui.DrawDeviceContext { 31 b.ui.dd.quit() 32 } 33 }
1 import ui 2 import ui.component as uic 3 4 const win_width = 600 5 const win_height = 400 6 7 fn main() { 8 window := ui.window( 9 width: win_width 10 height: win_height 11 title: 'V UI: Composable Widget' 12 mode: .resizable 13 native_message: false 14 layout: ui.column( 15 margin_: .05 16 spacing: .05 17 heights: [8 * ui.stretch, ui.stretch, ui.stretch] 18 children: [ 19 ui.row( 20 spacing: .1 21 margin_: 5 22 widths: ui.stretch 23 children: [ 24 uic.doublelistbox_stack( 25 id: 'dlb1' 26 title: 'dlb1' 27 items: [ 28 'totto', 29 'titi', 30 ] 31 ), 32 uic.doublelistbox_stack( 33 id: 'dlb2' 34 title: 'dlb2' 35 items: [ 36 'tottoooo', 37 'titi', 38 'tototta', 39 ] 40 ), 41 ] 42 ), 43 ui.button(id: 'btn1', text: 'get values for dlb1', on_click: btn_click), 44 ui.button(id: 'btn2', text: 'get values for dlb2', on_click: btn_click), 45 ] 46 ) 47 ) 48 ui.run(window) 49 } 50 51 fn btn_click(b &ui.Button) { 52 dlb := uic.doublelistbox_component_from_id(b.ui.window, if b.id == 'btn1' { 53 'dlb1' 54 } else { 55 'dlb2' 56 }) 57 res := 'result(s) : ${dlb.values()}' 58 println(res) 59 b.ui.window.message(res) 60 }
1 import ui 2 import ui.component as uic 3 // import gx 4 5 const win_width = 800 6 const win_height = 600 7 8 fn main() { 9 window := ui.window( 10 width: win_width 11 height: win_height 12 title: 'V UI: File Browser' 13 native_message: false 14 mode: .resizable 15 layout: uic.filebrowser_stack( 16 id: 'fb' 17 on_click_ok: on_click_ok 18 on_click_cancel: on_click_cancel 19 ) 20 ) 21 ui.run(window) 22 } 23 24 fn on_click_ok(b &ui.Button) { 25 println(uic.filebrowser_component(b).selected_full_title()) 26 } 27 28 fn on_click_cancel(b &ui.Button) { 29 if b.ui.dd is ui.DrawDeviceContext { 30 b.ui.dd.quit() 31 } 32 }
1 import ui 2 import ui.component as uic 3 import gx 4 5 struct App { 6 mut: 7 window &ui.Window = unsafe { nil } 8 log string 9 text string = 'il était une fois V ....\nLa vie est belle...' 10 } 11 12 fn main() { 13 mut app := &App{} 14 mut tb := ui.textbox( 15 id: 'tb' 16 text: &app.text 17 mode: .multiline 18 bg_color: gx.yellow 19 ) 20 mut dtw := ui.DrawTextWidget(tb) 21 dtw.update_style(size: 30, color: gx.red) 22 mut window := ui.window( 23 mode: .resizable 24 width: 800 25 height: 600 26 on_init: fn (win &ui.Window) { 27 mut btn := win.get_or_panic[ui.Button]('txt_color') 28 tb := win.get_or_panic[ui.TextBox]('tb') 29 unsafe { 30 (*btn.bg_color) = tb.text_styles.current.color 31 } 32 } 33 layout: ui.column( 34 margin_: 10 35 heights: [20.0, ui.stretch] 36 spacing: 10 37 children: [ 38 ui.row( 39 widths: ui.compact 40 spacing: 10 41 children: [ 42 uic.fontbutton( 43 text: 'font' 44 dtw: tb 45 ), 46 uic.colorbutton( 47 id: 'txt_color' 48 // bg_color: &tb.text_styles.current.color 49 // DO NOT REMOVE: more general alternative with callback 50 on_changed: fn (cbc &uic.ColorButtonComponent) { 51 mut tv := cbc.widget.ui.window.get_or_panic[ui.TextBox]('tb').tv 52 tv.update_style(color: cbc.bg_color) 53 } 54 ), 55 uic.colorbutton( 56 id: 'bg_color' 57 bg_color: &tb.style.bg_color 58 ), 59 ] 60 ), 61 tb, 62 ] 63 ) 64 ) 65 app.window = window 66 uic.fontchooser_subwindow_add(mut window) 67 uic.colorbox_subwindow_add(mut window) 68 ui.run(app.window) 69 }
1 import ui 2 import ui.component as uic 3 import gx 4 5 const win_width = 600 6 const win_height = 600 7 8 fn main() { 9 n := 1000000 10 window := ui.window( 11 width: win_width 12 height: win_height 13 title: 'V UI: Grid' 14 native_message: false 15 mode: .resizable 16 bg_color: gx.white 17 on_init: win_init 18 layout: uic.datagrid_stack( 19 id: 'grid' 20 is_focused: true 21 vars: { 22 'v1': ['toto', 'titi', 'tata'].repeat(n) 23 'v2': ['toti', 'tito', 'tato'].repeat(n) 24 'sex': uic.Factor{ 25 levels: ['Male', 'Female'] 26 values: [0, 0, 1].repeat(n) 27 } 28 'csp': uic.Factor{ 29 levels: ['job1', 'job2', 'other'] 30 values: [0, 1, 2].repeat(n) 31 } 32 'v3': ['toto', 'titi', 'tata'].repeat(n) 33 'v4': ['toti', 'tito', 'tato'].repeat(n) 34 'sex2': uic.Factor{ 35 levels: ['Male', 'Female'] 36 values: [0, 0, 1].repeat(n) 37 } 38 'csp2': uic.Factor{ 39 levels: ['job1', 'job2', 'other'] 40 values: [0, 1, 2].repeat(n) 41 } 42 } 43 ) 44 ) 45 ui.run(window) 46 } 47 48 fn win_init(w &ui.Window) { 49 // mut g := uic.grid_component_from_id(w, "grid") 50 // g.init_ranked_grid_data([2, 0], [1, -1]) 51 52 // mut gs := uic.gridsettings_component_from_id(w, "gs") 53 // println("gs id: <$gs.id> ${typeof(gs).name} $gsl.id") 54 // gs.update_sorted_vars() 55 }
1 import ui 2 import ui.component as uic 3 import os 4 5 const win_width = 500 6 const win_height = 500 7 8 fn main() { 9 window := ui.window( 10 width: win_width 11 height: win_height 12 title: 'Grid' 13 mode: .resizable 14 on_init: win_init 15 layout: ui.row( 16 children: [ 17 uic.rasterview_canvaslayout( 18 id: 'rv' 19 ), 20 ] 21 ) 22 ) 23 ui.run(window) 24 } 25 26 fn win_init(mut w ui.Window) { 27 mut rv := uic.rasterview_component_from_id(w, 'rv') 28 rv.load_image(os.resource_abs_path(os.join_path('../../assets/img', 'logo.png'))) 29 // rv.load_image('../assets/img/icons8-cursor-67.png') 30 }
1 import ui 2 import ui.component as uic 3 import gx 4 5 const win_width = 600 6 const win_height = 400 7 8 fn main() { 9 cb_layout := uic.colorbox_stack(id: 'cbox', light: false, hsl: false) 10 rect := ui.rectangle( 11 text: 'Here a simple ui rectangle ' 12 color: gx.blue 13 // align: gx.align_left 14 text_size: 30 15 ) 16 window := ui.window( 17 width: win_width 18 height: win_height 19 title: 'V UI: Toolbar' 20 mode: .resizable 21 native_message: false 22 layout: ui.column( 23 margin_: .05 24 spacing: .05 25 children: [ 26 uic.tabs_stack( 27 id: 'tab' 28 tabs: ['tab1', 'tab2', 'tab3'] 29 pages: [ 30 ui.column( 31 heights: ui.compact 32 widths: ui.compact 33 bg_color: gx.rgb(200, 100, 200) 34 children: [ 35 ui.button(id: 'left1', text: 'toto', padding: .1, radius: .25), 36 ui.button(id: 'left2', text: 'toto2'), 37 ] 38 ), 39 ui.column( 40 heights: ui.compact 41 widths: ui.compact 42 children: [ 43 cb_layout, 44 rect, 45 ] 46 ), 47 ui.column( 48 heights: 200.0 49 widths: 300.0 50 bg_color: gx.rgb(100, 200, 200) 51 children: [ 52 uic.doublelistbox_stack( 53 id: 'dlb1' 54 title: 'dlb1' 55 items: [ 56 'totto', 57 'titi', 58 ] 59 ), 60 ] 61 ), 62 ] 63 ), 64 ] 65 ) 66 ) 67 mut cb := uic.colorbox_component(cb_layout) 68 cb.connect(&rect.style.color) 69 ui.run(window) 70 }
1 import ui 2 import ui.component as uic 3 import gx 4 5 const win_width = 800 6 const win_height = 600 7 8 fn main() { 9 window := ui.window( 10 width: win_width 11 height: win_height 12 title: 'V UI: TreeView' 13 native_message: false 14 mode: .resizable 15 layout: ui.column( 16 scrollview: true 17 heights: ui.compact 18 children: [ 19 uic.treeview_stack( 20 id: 'demo' 21 incr_mode: true 22 trees: [ 23 uic.Tree{ 24 title: 'toto1' 25 items: [ 26 uic.TreeItem('file: ftftyty1'), 27 'file: hgyfyf1', 28 uic.Tree{ 29 title: 'tttytyty1' 30 items: [ 31 uic.TreeItem('file: tutu2'), 32 'file: ytytyy2', 33 ] 34 }, 35 ] 36 }, 37 uic.Tree{ 38 title: 'toto2' 39 items: [ 40 uic.TreeItem('file: ftftyty1'), 41 'file: hgyfyf1111', 42 ] 43 }, 44 uic.Tree{ 45 title: 'toto3' 46 items: [ 47 uic.TreeItem('file: ftftyty2'), 48 'file: hgyfyf2222', 49 ] 50 }, 51 ] 52 icons: { 53 'folder': 'tata' 54 'file': 'toto' 55 } 56 text_color: gx.blue 57 on_click: treeview_on_click 58 ), 59 ] 60 ) 61 ) 62 ui.run(window) 63 } 64 65 fn treeview_on_click(c &ui.CanvasLayout, mut tv uic.TreeViewComponent) { 66 selected := c.id 67 println('${selected} selected with title: ${tv.titles[selected]}!') 68 }
1 import ui 2 import gx 3 4 const win_width = 200 5 const win_height = 40 6 7 @[heap] 8 struct App { 9 mut: 10 counter string = '0' 11 } 12 13 fn main() { 14 mut app := &App{} 15 window := ui.window( 16 width: win_width 17 height: win_height 18 title: 'Counter' 19 mode: .resizable 20 layout: ui.row( 21 spacing: 5 22 margin_: 10 23 widths: ui.stretch 24 heights: ui.stretch 25 children: [ 26 ui.textbox( 27 max_len: 20 28 // height: 30 29 read_only: true 30 is_numeric: true 31 text: &app.counter 32 ), 33 ui.button( 34 text: 'Count' 35 bg_color: gx.light_gray 36 radius: 5 37 border_color: gx.gray 38 on_click: app.btn_click 39 ), 40 ] 41 ) 42 ) 43 ui.run(window) 44 } 45 46 fn (mut app App) btn_click(btn &ui.Button) { 47 app.counter = (app.counter.int() + 1).str() 48 }
1 import ui 2 import gx 3 4 const win_width = 200 5 const win_height = 40 6 7 @[heap] 8 struct App { 9 mut: 10 counter string = '0' 11 } 12 13 fn main() { 14 mut app := &App{} 15 window := ui.window( 16 width: win_width 17 height: win_height 18 title: 'Counter' 19 mode: .resizable 20 layout: ui.row( 21 spacing: 5 22 margin_: 10 23 widths: ui.stretch 24 heights: ui.stretch 25 children: [ 26 ui.textbox( 27 max_len: 20 28 // height: 30 29 read_only: true 30 is_numeric: true 31 text: &app.counter 32 ), 33 ui.button( 34 text: 'Count' 35 bg_color: gx.light_gray 36 radius: 5 37 border_color: gx.gray 38 on_click: fn [mut app] (btn &ui.Button) { 39 cpt := app.counter.int() + 1 40 app.counter = cpt.str() 41 } 42 ), 43 ] 44 ) 45 ) 46 ui.run(window) 47 }
1 import ui 2 import regex 3 import gx 4 import math 5 6 const win_width = 400 7 const win_height = 41 8 9 fn main() { 10 window := ui.window( 11 width: win_width 12 height: win_height 13 title: 'Temperature Converter' 14 mode: .resizable 15 layout: ui.row( 16 margin_: 10 17 spacing: 10 18 widths: [ui.stretch, ui.compact, ui.stretch, ui.compact] 19 heights: 20.0 20 children: [ 21 ui.textbox( 22 id: 'celsius' 23 on_change: on_change_celsius 24 ), 25 ui.label(text: 'Celsius = '), 26 ui.textbox( 27 id: 'fahren' 28 on_change: on_change_fahren 29 ), 30 ui.label(text: 'Fahrenheit'), 31 ] 32 ) 33 ) 34 ui.run(window) 35 } 36 37 fn on_change_celsius(mut tb_celsius ui.TextBox) { 38 mut tb_fahren := tb_celsius.ui.window.get_or_panic[ui.TextBox]('fahren') 39 if tb_celsius.text.len <= 0 { 40 tb_fahren.set_text('0') 41 return 42 } 43 if is_number(*(tb_celsius.text)) { 44 celsius := (*(tb_celsius.text)).f64() 45 fahren := celsius * (9.0 / 5.0) + 32.0 46 tb_fahren.set_text((math.ceil(fahren * 100) / 100.0).str()) 47 tb_celsius.update_style(bg_color: gx.white) 48 } else { 49 tb_celsius.update_style(bg_color: gx.orange) 50 } 51 } 52 53 fn on_change_fahren(mut tb_fahren ui.TextBox) { 54 mut tb_celsius := tb_fahren.ui.window.get_or_panic[ui.TextBox]('celsius') 55 if tb_fahren.text.len <= 0 { 56 tb_celsius.set_text('0') 57 return 58 } 59 if is_number(*(tb_fahren.text)) { 60 fah := (*tb_fahren.text).f64() 61 cel := (fah - 32.0) * (5.0 / 9.0) 62 tb_celsius.set_text((math.ceil(cel * 100) / 100.0).str()) 63 tb_fahren.update_style(bg_color: gx.white) 64 } else { 65 tb_fahren.update_style(bg_color: gx.orange) 66 } 67 } 68 69 fn is_number(txt string) bool { 70 query := r'\-?(?P<before>\d+)\.?(?P<after>\d+)?' 71 mut re := regex.regex_opt(query) or { panic(err) } 72 return re.matches_string(txt) 73 }
1 import ui 2 import time 3 import math 4 import gx 5 6 const win_width = 287 7 const win_height = 155 8 const duration = 1 // ms 9 10 const left = 60.0 11 12 @[heap] 13 struct App { 14 mut: 15 lbl_elapsed_value &ui.Label 16 progress_bar &ui.ProgressBar 17 slider &ui.Slider = unsafe { nil } 18 window &ui.Window 19 duration f64 = 15.0 20 elapsed_time f64 = 0.0 21 } 22 23 fn main() { 24 mut app := &App{ 25 lbl_elapsed_value: ui.label(text: '00.0s', text_size: 1.0 / 10) 26 progress_bar: ui.progressbar( 27 height: 20 28 val: 0 29 max: 100 30 color: gx.green 31 border_color: gx.dark_green 32 ) 33 window: unsafe { nil } 34 } 35 app.slider = ui.slider( 36 width: 180 37 height: 20 38 orientation: .horizontal 39 max: 30 40 min: 0 41 val: 15.0 42 on_value_changed: app.on_value_changed 43 ) 44 window := ui.window( 45 width: win_width 46 height: win_height 47 title: 'Timer' 48 mode: .resizable 49 layout: ui.column( 50 margin_: .05 51 spacing: .05 52 children: [ 53 ui.row( 54 spacing: .1 55 widths: [left, ui.stretch] 56 children: [ui.label(text: 'Elapsed Time:', text_size: 1.0 / 10), app.progress_bar] 57 ), 58 ui.row( 59 spacing: .1 60 widths: [left, ui.stretch] 61 children: [ui.spacing(), app.lbl_elapsed_value] 62 ), 63 ui.row( 64 spacing: .1 65 widths: [left, ui.stretch] 66 children: [ui.label(text: 'Duration:', text_size: 1.0 / 10), app.slider] 67 ), 68 ui.button(text: 'Reset', on_click: app.on_reset), 69 ] 70 ) 71 ) 72 app.window = window 73 74 // go app.timer() 75 ui.run(window) 76 } 77 78 fn (mut app App) on_value_changed(slider &ui.Slider) { 79 app.duration = app.slider.val 80 } 81 82 fn (mut app App) on_reset(button &ui.Button) { 83 app.elapsed_time = 0.0 84 spawn app.timer() 85 } 86 87 fn (mut app App) timer() { 88 for { 89 if app.elapsed_time == app.duration { 90 break 91 } 92 if app.elapsed_time > app.duration { 93 app.elapsed_time = app.duration 94 } else { 95 app.elapsed_time += 0.1 * duration 96 } 97 app.lbl_elapsed_value.set_text('${math.ceil(app.elapsed_time * 100) / 100}s') 98 if app.duration == 0 { 99 app.progress_bar.val = 100 100 } else { 101 app.progress_bar.val = int(app.elapsed_time * 100.0 / app.duration) 102 } 103 time.sleep(100000 * duration * time.microsecond) 104 app.window.refresh() 105 } 106 }
1 import ui 2 import gx 3 import math 4 5 const default_radius = 20 6 7 // Circle 8 9 struct Circle { 10 x f32 11 y f32 12 mut: 13 radius f32 14 } 15 16 fn (c Circle) contains(x f32, y f32) bool { 17 return math.pow((c.x - x), 2) + math.pow((c.y - y), 2) <= math.pow(c.radius, 2) 18 } 19 20 struct State { 21 mut: 22 circles []Circle 23 current_action int = -1 24 history []Action 25 } 26 27 fn (mut state State) add_action(action Action) { 28 state.history << action 29 state.current_action += 1 30 } 31 32 fn (mut state State) reset_history() { 33 state.history.delete_many(state.current_action, state.history.len - state.current_action - 1) 34 // println(state.history) 35 } 36 37 fn (mut state State) undo() { 38 if state.current_action >= 0 { 39 state.history[state.current_action].undo(mut state) 40 state.current_action -= 1 41 } 42 } 43 44 fn (mut state State) redo() { 45 if state.current_action < state.history.len - 1 { 46 state.current_action += 1 47 state.history[state.current_action].do(mut state) 48 } 49 } 50 51 fn (state State) point_inside(x f32, y f32) int { 52 mut sel := -1 53 for i, circle in state.circles { 54 if circle.contains(x, y) { 55 sel = i 56 break 57 } 58 } 59 return sel 60 } 61 62 // Action 63 64 interface Action { 65 do(mut state State) 66 undo(mut state State) 67 } 68 69 struct ActionAddCircle { 70 circle Circle 71 } 72 73 fn (a ActionAddCircle) do(mut state State) { 74 state.circles << a.circle 75 } 76 77 fn (a ActionAddCircle) undo(mut state State) { 78 if state.circles.len > 0 { 79 state.circles.delete_last() 80 } else { 81 println('Warning: no circle to delete') 82 } 83 } 84 85 struct ActionSetCircleRadius { 86 circle_index int 87 new_radius f32 88 old_radius f32 89 } 90 91 fn (a ActionSetCircleRadius) do(mut state State) { 92 state.circles[a.circle_index].radius = a.new_radius 93 } 94 95 fn (a ActionSetCircleRadius) undo(mut state State) { 96 state.circles[a.circle_index].radius = a.old_radius 97 } 98 99 // App 100 101 @[heap] 102 struct App { 103 mut: 104 sel int 105 sel_radius f32 106 hover int 107 state State 108 } 109 110 fn main() { 111 app := &App{} 112 sw_radius := ui.subwindow( 113 id: 'sw_radius' 114 layout: ui.column( 115 width: 200 116 height: 60 117 margin_: 10 118 spacing: 10 119 bg_color: ui.alpha_colored(gx.light_gray, 100) 120 widths: ui.stretch 121 heights: ui.compact 122 children: [ 123 ui.label(text: 'Adjust radius', justify: ui.center_center), 124 ui.slider( 125 id: 'sl_radius' 126 orientation: .horizontal 127 max: 50 128 val: 20 129 on_value_changed: app.slider_changed 130 ), 131 ] 132 ) 133 ) 134 mut window := ui.window( 135 width: 500 136 height: 400 137 title: 'Circle drawer' 138 mode: .resizable 139 layout: ui.column( 140 spacing: 10 141 margin_: 20 142 widths: ui.stretch 143 heights: [ui.compact, ui.stretch] 144 children: [ 145 ui.row( 146 spacing: 20 147 widths: [ui.stretch, 40, 40, ui.stretch] 148 children: [ui.spacing(), 149 ui.button( 150 id: 'btn_undo' 151 text: 'Undo' 152 radius: 5 153 on_click: app.click_undo 154 ), 155 ui.button( 156 id: 'btn_redo' 157 text: 'Redo' 158 radius: 5 159 on_click: app.click_redo 160 ), 161 ui.spacing()] 162 ), 163 ui.canvas_plus( 164 bg_color: gx.white 165 bg_radius: .025 166 clipping: true 167 on_draw: app.draw_circles 168 on_click: app.click_circles 169 on_mouse_move: app.mouse_move_circles 170 ), 171 ] 172 ) 173 ) 174 window.subwindows << sw_radius 175 ui.run(window) 176 } 177 178 fn (app &App) draw_circles(mut d ui.DrawDevice, c &ui.CanvasLayout) { 179 for i, circle in app.state.circles { 180 if i == app.hover { 181 c.draw_device_circle_filled(d, circle.x, circle.y, circle.radius, gx.light_gray) 182 } 183 c.draw_device_circle_empty(d, circle.x, circle.y, circle.radius, gx.black) 184 } 185 } 186 187 fn (mut app App) click_circles(c &ui.CanvasLayout, e ui.MouseEvent) { 188 mut sw := c.ui.window.get_or_panic[ui.SubWindow]('sw_radius') 189 if c.ui.btn_down[0] { 190 if sw.is_visible() { 191 sw.set_visible(false) 192 sl := c.ui.window.get_or_panic[ui.Slider]('sl_radius') 193 action := ActionSetCircleRadius{ 194 circle_index: app.sel 195 new_radius: f32(sl.val) 196 old_radius: app.sel_radius 197 } 198 app.state.add_action(action) 199 action.do(mut app.state) 200 } else { 201 radius := default_radius 202 circle := Circle{f32(e.x), f32(e.y), f32(radius)} 203 action := ActionAddCircle{circle} 204 app.state.add_action(action) 205 app.state.reset_history() // clear the end of history from the current action 206 action.do(mut app.state) 207 } 208 } else if c.ui.btn_down[1] { 209 app.sel = app.state.point_inside(f32(e.x), f32(e.y)) 210 if app.sel >= 0 { 211 app.sel_radius = app.state.circles[app.sel].radius 212 sw.set_visible(true) 213 sw.set_pos(e.x + int(app.sel_radius), e.y + int(app.sel_radius)) 214 sw.update_layout() 215 } 216 } 217 mut btn_undo := c.ui.window.get_or_panic[ui.Button]('btn_undo') 218 check_undo_disabled(app.state, mut btn_undo) 219 mut btn_redo := c.ui.window.get_or_panic[ui.Button]('btn_redo') 220 check_redo_disabled(app.state, mut btn_redo) 221 } 222 223 fn (mut app App) mouse_move_circles(c &ui.CanvasLayout, e ui.MouseMoveEvent) { 224 app.hover = app.state.point_inside(f32(e.x), f32(e.y)) 225 } 226 227 fn (mut app App) click_undo(mut b ui.Button) { 228 if !b.ui.btn_down[0] { 229 return 230 } 231 app.state.undo() 232 check_undo_disabled(app.state, mut b) 233 mut redo := b.ui.window.get_or_panic[ui.Button]('btn_redo') 234 check_redo_disabled(app.state, mut redo) 235 } 236 237 fn (mut app App) click_redo(mut b ui.Button) { 238 if !b.ui.btn_down[0] { 239 return 240 } 241 app.state.redo() 242 check_redo_disabled(app.state, mut b) 243 mut undo := b.ui.window.get_or_panic[ui.Button]('btn_undo') 244 check_undo_disabled(app.state, mut undo) 245 } 246 247 fn check_undo_disabled(state State, mut undo ui.Button) { 248 undo.disabled = state.current_action == -1 249 } 250 251 fn check_redo_disabled(state State, mut redo ui.Button) { 252 redo.disabled = state.current_action == state.history.len - 1 253 } 254 255 fn (mut app App) slider_changed(sl &ui.Slider) { 256 app.state.circles[app.sel].radius = sl.val 257 }
1 import ui 2 import gx 3 import time 4 5 const no_time = time.Time{} 6 7 @[heap] 8 struct App { 9 mut: 10 dd_flight &ui.Dropdown = unsafe { nil } 11 tb_oneway &ui.TextBox = unsafe { nil } 12 tb_return &ui.TextBox = unsafe { nil } 13 btn_book &ui.Button = unsafe { nil } 14 } 15 16 fn main() { 17 app := &App{} 18 window := ui.window( 19 width: 200 20 height: 110 21 title: 'Flight booker' 22 mode: .resizable 23 on_init: app.win_init 24 layout: ui.column( 25 spacing: 5 26 margin_: 5 27 // widths: ui.stretch 28 // heights: ui.stretch 29 children: [ 30 ui.dropdown( 31 id: 'dd_flight' 32 z_index: 10 33 selected_index: 0 34 on_selection_changed: app.dd_change 35 items: [ 36 ui.DropdownItem{ 37 text: 'one-way flight' 38 }, 39 ui.DropdownItem{ 40 text: 'return flight' 41 }, 42 ] 43 ), 44 ui.textbox(id: 'tb_oneway', on_change: app.tb_change), 45 ui.textbox(id: 'tb_return', read_only: true, on_change: app.tb_change), 46 ui.button( 47 id: 'btn_book' 48 text: 'Book' 49 radius: 5 50 bg_color: gx.light_gray 51 on_click: app.btn_book_click 52 ), 53 ] 54 ) 55 ) 56 ui.run(window) 57 } 58 59 fn (mut app App) win_init(win &ui.Window) { 60 app.dd_flight = win.get_or_panic[ui.Dropdown]('dd_flight') 61 app.tb_oneway = win.get_or_panic[ui.TextBox]('tb_oneway') 62 app.tb_return = win.get_or_panic[ui.TextBox]('tb_return') 63 app.btn_book = win.get_or_panic[ui.Button]('btn_book') 64 // init dates 65 t := time.now() 66 date := '${t.day}.${t.month}.${t.year}' 67 app.tb_oneway.set_text(date.clone()) 68 app.tb_return.set_text(date.clone()) 69 } 70 71 fn (mut app App) dd_change(dd &ui.Dropdown) { 72 match dd.selected().text { 73 'one-way flight' { 74 app.tb_return.read_only = true 75 } 76 else { 77 app.tb_return.read_only = false 78 } 79 } 80 } 81 82 fn (mut app App) tb_change(mut tb ui.TextBox) { 83 valid := valid_date(tb.text) 84 app.btn_book.disabled = !valid 85 tb.update_style( 86 bg_color: if valid { gx.white } else { gx.orange } 87 ) 88 } 89 90 fn (app &App) btn_book_click(btn &ui.Button) { 91 msg := if app.dd_flight.selected().text == 'one-way flight' { 92 'You have booked a one-way flight for ${*(app.tb_oneway.text)}' 93 } else { 94 'You have booked a return flight from ${*(app.tb_oneway.text)} to ${*(app.tb_return.text)}' 95 } 96 btn.ui.window.message(msg) 97 } 98 99 fn valid_date(date string) bool { 100 mut day, mut month, mut year := 'DDDDD', 'MMMMM', 'YYYYY' 101 dmy := date.split('.') 102 if dmy.len > 0 { 103 day = dmy[0] 104 } 105 if dmy.len > 1 { 106 month = dmy[1] 107 } 108 if dmy.len > 2 { 109 year = dmy[2] 110 } 111 // YYYY-MM-DD HH:mm:ss 112 ts := '${year}-${month}-${day} 00:00:00' 113 t := time.parse(ts) or { no_time } 114 // println("$t.day/$t.month/$t.year") 115 nd := time.days_in_month(t.month, t.year) or { -1 } 116 return t != no_time && t.day <= nd 117 }
1 import ui 2 3 struct Person { 4 id string 5 name string 6 surname string 7 } 8 9 @[heap] 10 struct App { 11 mut: 12 people []Person 13 lb_people &ui.ListBox = unsafe { nil } 14 tb_filter &ui.TextBox = unsafe { nil } 15 tb_name &ui.TextBox = unsafe { nil } 16 tb_surname &ui.TextBox = unsafe { nil } 17 } 18 19 fn main() { 20 app := &App{ 21 people: [ 22 person('Iron', 'Man'), 23 person('Bat', 'Man'), 24 person('James', 'Bond'), 25 person('Super', 'Man'), 26 person('Cat', 'Woman'), 27 person('Wonder', 'Woman'), 28 ] 29 } 30 window := ui.window( 31 width: 400 32 height: 300 33 title: 'CRUD' 34 mode: .resizable 35 on_init: app.win_init 36 layout: ui.column( 37 spacing: 5 38 margin_: 10 39 widths: ui.stretch 40 heights: [ui.compact, ui.stretch, ui.compact] 41 children: [ 42 ui.row( 43 widths: ui.stretch 44 children: [ 45 ui.row( 46 widths: [70.0, ui.stretch] 47 children: [ui.label(text: 'Filter prefix:', justify: ui.center_left), 48 ui.textbox(id: 'tb_filter', on_change: app.on_change_filter)] 49 ), 50 ui.spacing(), 51 ] 52 ), 53 ui.row( 54 widths: ui.stretch 55 children: [ 56 ui.listbox( 57 id: 'lb_people' 58 ), 59 ui.column( 60 margin_: 5 61 spacing: 5 62 heights: ui.compact 63 children: [ 64 ui.row( 65 widths: [60.0, ui.stretch] 66 children: [ui.label(text: 'Name:', justify: ui.center_left), 67 ui.textbox(id: 'tb_name')] 68 ), 69 ui.row( 70 widths: [60.0, ui.stretch] 71 children: [ 72 ui.label( 73 text: 'Surname:' 74 justify: ui.center_left 75 ), 76 ui.textbox(id: 'tb_surname'), 77 ] 78 ), 79 ] 80 ), 81 ] 82 ), 83 ui.row( 84 margin_: 5 85 spacing: 10 86 widths: ui.compact 87 heights: 30.0 88 children: [ 89 ui.button( 90 id: 'btn_create' 91 text: 'Create' 92 radius: 5 93 on_click: app.btn_create_click 94 ), 95 ui.button( 96 id: 'btn_update' 97 text: 'Update' 98 radius: 5 99 on_click: app.btn_update_click 100 ), 101 ui.button( 102 id: 'btn_delete' 103 text: 'Delete' 104 radius: 5 105 on_click: app.btn_delete_click 106 ), 107 ] 108 ), 109 ] 110 ) 111 ) 112 ui.run(window) 113 } 114 115 fn (mut app App) win_init(win &ui.Window) { 116 // init app fields 117 app.lb_people = win.get_or_panic[ui.ListBox]('lb_people') 118 app.tb_filter = win.get_or_panic[ui.TextBox]('tb_filter') 119 app.tb_name = win.get_or_panic[ui.TextBox]('tb_name') 120 app.tb_surname = win.get_or_panic[ui.TextBox]('tb_surname') 121 // init listbox content 122 app.update_listbox() 123 } 124 125 fn (mut app App) on_change_filter(mut tb ui.TextBox) { 126 app.update_listbox() 127 } 128 129 fn (mut app App) btn_create_click(btn &ui.Button) { 130 p := person(app.tb_name.text, app.tb_surname.text) 131 if p.id !in app.people.map(it.id) { 132 app.people << p 133 } 134 app.update_listbox() 135 } 136 137 fn (mut app App) btn_update_click(btn &ui.Button) { 138 app.update_selected_person() 139 } 140 141 fn (mut app App) btn_delete_click(btn &ui.Button) { 142 app.delete_selected_person() 143 } 144 145 fn (mut app App) update_listbox() { 146 mut name := '' 147 filter := *(app.tb_filter.text) 148 app.lb_people.reset() 149 for p in app.people { 150 name = person_name(p.name, p.surname) 151 if filter == '' || name[0..filter.len] == filter { 152 app.lb_people.add_item(p.id, name) 153 } 154 } 155 } 156 157 fn (mut app App) update_selected_person() { 158 id, _ := app.lb_people.selected_item() 159 if id != '' { 160 for i, p in app.people { 161 if p.id == id { 162 app.people[i] = person(app.tb_name.text, app.tb_surname.text) 163 } 164 } 165 app.update_listbox() 166 } 167 } 168 169 fn (mut app App) delete_selected_person() { 170 id, _ := app.lb_people.selected_item() 171 if id != '' { 172 for i, p in app.people { 173 if p.id == id { 174 app.people.delete(i) 175 } 176 } 177 app.update_listbox() 178 } 179 } 180 181 fn person(name string, surname string) Person { 182 return Person{id_name(name, surname), name, surname} 183 } 184 185 fn person_name(name string, surname string) string { 186 return '${surname}, ${name}' 187 } 188 189 fn id_name(name string, surname string) string { 190 return '${name}_${surname}' 191 }
1 import ui 2 import ui.component as uic 3 4 fn main() { 5 // A 6 mut vars := { 7 'A': uic.GridData([''].repeat(100)) 8 } 9 // from B to Z 10 for i in 66 .. (66 + 25) { 11 vars[[u8(i)].bytestr()] = uic.GridData([''].repeat(100)) 12 } 13 14 // Init some values 15 mut v_a := vars['A'] or { []string{} } 16 if mut v_a is []string { 17 v_a[0] = 'Sum B2:C5 = ' 18 } 19 mut v_b := vars['B'] or { []string{} } 20 if mut v_b is []string { 21 v_b[1] = '12' 22 v_b[2] = '1' 23 v_b[3] = '1' 24 v_b[4] = '23' 25 } 26 mut v_c := vars['C'] or { []string{} } 27 if mut v_c is []string { 28 v_c[1] = '13' 29 v_c[2] = '-1' 30 v_c[3] = '31' 31 } 32 mut v_d := vars['D'] or { []string{} } 33 if mut v_d is []string { 34 v_d[1] = '3' 35 v_d[2] = '10' 36 v_d[3] = '1' 37 v_d[4] = '24' 38 } 39 window := ui.window( 40 width: 600 41 height: 400 42 title: 'Cells' 43 mode: .resizable 44 layout: ui.row( 45 spacing: 5 46 margin_: 10 47 widths: ui.stretch 48 heights: ui.stretch 49 children: [ 50 uic.datagrid_stack( 51 id: 'dgs' 52 vars: vars 53 formulas: { 54 'B1': '=sum(B2:C5, D2)' 55 'C5': '=sum(D2:D5)' 56 'A4': '=sum(B4:D4)' 57 } 58 is_focused: true 59 ), 60 ] 61 ) 62 ) 63 ui.run(window) 64 }