Gallery

Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }
Vlang Code
image
   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  }