From 9e09f02a69b66c1679495a9d9af10c52a0447169 Mon Sep 17 00:00:00 2001 From: lmp Date: Thu, 14 Dec 2023 23:03:34 +0100 Subject: [PATCH] ui: better example now that embeds in interfaces bug has been fixed in V --- README.md | 2 +- examples/ui/ui.v | 96 +++++++++++++++++++------------------------ ui/event.v | 1 - ui/item.v | 105 +++++++++++++++++++++++++++++++++++++++-------- ui/node.v | 31 ++++++++++---- 5 files changed, 154 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index bb2be7d..91a4374 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Windows, macOS, Linux, Raspberry PI, Android, Web (WASM/emscripten) and likely m music, sounds etc. * [WIP] Export to different platforms via the `shy export` command. * [WIP] Intuitive Qt/Widgets/QML (scene graph) inspired `ui` module - supporting *custom* UI items (currently full of BUGS). + supporting *custom* UI items. * [WIP] ... much more :) # Currently known downsides diff --git a/examples/ui/ui.v b/examples/ui/ui.v index a325582..b688b2a 100644 --- a/examples/ui/ui.v +++ b/examples/ui/ui.v @@ -32,6 +32,10 @@ pub fn (m &MyUIItem) draw(ui_ &ui.UI) { m.EventArea.draw(ui_) } +pub fn (m &MyUIItem) visual_state() ui.VisualState { + return m.EventArea.visual_state() +} + pub fn (m &MyUIItem) event(e ui.Event) ?&ui.Node { // println('MyUIItem event called ${m.id}') return m.EventArea.event(e) @@ -50,6 +54,10 @@ pub fn (m &MyRect) draw(ui_ &ui.UI) { m.Rectangle.draw(ui_) } +pub fn (m &MyRect) visual_state() ui.VisualState { + return m.Rectangle.visual_state() +} + pub fn (m &MyRect) event(e ui.Event) ?&ui.Node { // println('MyRect event called ${m.id}') return m.Rectangle.event(e) @@ -67,6 +75,7 @@ pub fn (mut a App) init() ! { height: 100 body: [ &ui.Rectangle{ + id: 43 x: 50 y: 50 width: 50 @@ -78,94 +87,91 @@ pub fn (mut a App) init() ! { width: 25 height: 25 body: [ - /* &MyRect{ id: 800 width: 20 on_event: [ fn (n &ui.Node, e ui.Event) bool { node := &&MyRect(n) // TODO this is a weird V quirk - eprintln('MyRect in call: ${ptr_str(n)}') + // eprintln('MyRect in call: ${ptr_str(n)} VS ${ptr_str(node)}') assert node.id == n.id - // println('MyUIItem on_event reached ${node.id}/${n.id}') - // mut mmui := unsafe { node } - // mmui.width++ - // println('${@STRUCT}/MyUIItem on_event was called ${mmui.width}') + mut mmui := unsafe { node } + if mmui.width < 80 { + mmui.width += 0.1 + } else if mmui.width >= 50 { + mmui.width = 25 + } // return true return false }, ] }, - */ - /* &MyUIItem{ id: 400 width: 10 on_event: [ fn (n &ui.Node, e ui.Event) bool { node := &&MyUIItem(n) // TODO this is a weird V quirk - eprintln('MyUIItem this in call: ${ptr_str(n)}') + // eprintln('MyUIItem this in call: ${ptr_str(n)} VS ${ptr_str(node)}') assert node.id == n.id // println('MyUIItem on_event reached ${node.id}/${n.id}') - // mut mmui := unsafe { node } - // mmui.width++ + mut mmui := unsafe { node } + mmui.width += 0.01 // println('${@STRUCT}/MyUIItem on_event was called ${mmui.width}') // return true return false }, ] - },*/ + }, ] }, ] }, - /* &ui.PointerEventArea{ id: 100 x: 50 y: 50 width: 500 height: 500 - /* on_event: [ fn (n &ui.Node, e ui.Event) bool { - //println('ui.EventArea on_event reached ${n.id}') + // println('ui.EventArea on_event reached ${n.id}') ea := &&ui.EventArea(n) assert ea.id == n.id mut mea := unsafe { ea } println('EventArea ${mea.id} found .x: ${mea.x}') // mea.x += 1 - // return true - return false + return true + // return false }, - ]*/ + ] on_pointer_event: [ fn (n &ui.Node, e ui.PointerEvent) bool { // println('PointerEventArea on_pointer_event reached ${n.id}') // TODO BUG V mixes up the pointer here see also `shy/ui/item.v` etc. // dump(typeof(n)) - /* + mut pea := &&ui.PointerEventArea(n) eprintln('PointerEventArea n in call: ${ptr_str(n)}') - eprintln('PointerEventArea this in call: ${ptr_str(n.this)}') + // eprintln('PointerEventArea this in call: ${ptr_str(n.this)}') eprintln('PointerEventArea &n in call: ${ptr_str(&n)}') eprintln('PointerEventArea pea in call: ${ptr_str(pea)}') // TODO `n &ui.Node` -> `n` pointer is not the same as in `ui/item.v`???? assert pea.id == n.id, 'HARD TO REPRODUCE V BUG' println('PointerEventArea ${pea.id} found .x: ${pea.x}') - pea.x += 1 + // pea.x += 1 // mut mpea := unsafe { pea } // println('PointerEventArea ${mpea.id} found .x: ${mpea.x}') // mpea.x += 1 // return true - */ + return false }, ] @@ -211,7 +217,7 @@ pub fn (mut a App) init() ! { ] }, ] - },*/ + }, ] } a.ui = ui.new( @@ -234,37 +240,23 @@ pub fn (mut a App) event(e shy.Event) { ui_event := ui.shy_to_ui_event(e) or { panic('${@STRUCT}.${@FN}: ${err}') } if handled_by_node := a.ui.event(ui_event) { - // printing the whole node will make things crash at runtime... + // TODO BUG printing the whole node will make things crash at runtime... println('Event was handled by ui.Node.id(${handled_by_node.id})') - } else { } // Mutability test a.ui.modify(fn (mut n ui.Node) { - // TODO BUG broken - V doesn't allow for retreiving back the original pointer of a *custom* type implementing ui.Node... - // The horrible thing is that it ~works... sometimes :( - // mut maybe := ui.cast[MyRect](n) - // - - /* - if n.id == 420 { - mut my_rect := &&MyRect(n) - // println('oi ${maybe}') - my_rect.x += 0.5 - my_rect.y += 0.25 + if mut n is MyRect { + println('oi ${n.id}') + if n.id == 420 { + n.x += 0.5 + n.y += 0.25 + } else { + n.y += 0.1 + } } - */ - - // mut maybe := &&MyRect(n) - // println('oi ${maybe}') - // if maybe.id == 420 { - // // println('oi ${maybe}') - // maybe.x += 0.5 - // maybe.y += 0.25 - //} - /* + if mut n is ui.Rectangle { - // mut n := unsafe { node } if n.id == 42 { if n.x < 200 { n.x += 1 @@ -272,14 +264,12 @@ pub fn (mut a App) event(e shy.Event) { n.x = 0 } } - // pid := n.parent() or { 0 }.id - // pid := n.parent().id if n.id == 43 { - // ppid := n.parent() or { 0 }.id - // ppid := n.parent().id - // println('${pid} vs. ${ppid}') + // pid := n.parent() or { -1 }.id + pid := n.parent().id + println('parent id ${pid}') } - }*/ + } }) /* diff --git a/ui/event.v b/ui/event.v index 9ad28f4..1b72982 100644 --- a/ui/event.v +++ b/ui/event.v @@ -17,7 +17,6 @@ pub enum ButtonState { pub struct UIEvent { pub: timestamp u64 - // window &Window // Since ui is based on shy.easy we have only one window } pub struct PointerEvent { diff --git a/ui/item.v b/ui/item.v index b26889b..089da31 100644 --- a/ui/item.v +++ b/ui/item.v @@ -16,7 +16,7 @@ import shy.lib as shy pub struct Item { shy.Rect pub: - id u64 + id u64 = 1 mut: // NOTE The `unsafe { nil }` assignment once resulted in several V bugs: https://github.com/vlang/v/issues/16882 // ... which was quickly fixed but one of them couldn't be made as an MRE (minimal reproducible example) - so it's @@ -29,10 +29,18 @@ mut: // parent returns this `Item`'s parent. pub fn (i &Item) parent() &Node { assert i != unsafe { nil } - // TODO returning ?&Node is not possible currently: if isnil(i.parent) { return none } + // TODO BUG returning ?&Node is not possible currently: if isnil(i.parent) { return none } return i.parent } +// reparent sets this `Item`'s `parent` to `new_parent`. +pub fn (i &Item) reparent(new_parent &Node) { + assert i != unsafe { nil } + unsafe { + i.parent = new_parent + } +} + // draw draws the `Item` and/or any child nodes. pub fn (i &Item) draw(ui &UI) { for child in i.body { @@ -40,7 +48,26 @@ pub fn (i &Item) draw(ui &UI) { } } -// event sends an `Event` any child nodes and/or it's own listeners. +// visual_state returns this `Item`'s `VisualState`. +@[inline] +pub fn (i &Item) visual_state() VisualState { + assert i != unsafe { nil } + p := i.parent() + if !isnil(p) && p.id != 0 { + p_vs := p.visual_state() + return VisualState{ + x: p_vs.x + i.Rect.x + y: p_vs.y + i.Rect.y + width: i.Rect.width + height: i.Rect.height + } + } + return VisualState{ + Rect: i.Rect + } +} + +// event delegates `e` `Event` to any child nodes and/or it's own listeners. pub fn (i &Item) event(e Event) ?&Node { // By sending the event on to the children nodes // it's effectively *bubbling* the event upwards in the @@ -50,12 +77,26 @@ pub fn (i &Item) event(e Event) ?&Node { return node } } - for on_event in i.on_event { - assert !isnil(on_event) - if on_event(i, e) { - // If `on_event` returns true, it means - // a listener on *this* item has accepted the event - return i + + hit := match e { + MouseButtonEvent, MouseMotionEvent, MouseWheelEvent { + vs := i.visual_state() + vs.Rect.contains(e.x, e.y) + } + else { + true + } + } + + if hit { + for on_event in i.on_event { + assert !isnil(on_event) + + if on_event(i, e) { + // If `on_event` returns true, it means + // a listener on *this* item has accepted the event + return i + } } } return none @@ -76,50 +117,72 @@ pub struct Rectangle { Item } +/* // parent returns the parent Node. pub fn (r &Rectangle) parent() &Node { return r.Item.parent() } - +*/ // draw draws the `Item` and/or any child nodes. pub fn (r &Rectangle) draw(ui &UI) { - // println('${@STRUCT}.${@FN} ${ptr_str(r)}') - // println('${@STRUCT}.${@FN} ${r}') + p_vs := r.visual_state() er := ui.easy.rect( - x: r.x - y: r.y - width: r.width - height: r.height + x: p_vs.x + y: p_vs.y + width: p_vs.width + height: p_vs.height ) er.draw() - + // Draw rest of tree (children) on top r.Item.draw(ui) } +/* +// visual_state returns this `Item`'s `VisualState`. +@[inline] +pub fn (r &Rectangle) visual_state() VisualState { + return r.Item.visual_state() +} +*/ + +/* // event sends an `Event` any child nodes and/or it's own listeners. pub fn (r &Rectangle) event(e Event) ?&Node { return r.Item.event(e) -} +}*/ @[heap] pub struct EventArea { Item } +/* // parent returns the parent Node. pub fn (ea &EventArea) parent() &Node { return ea.Item.parent() } +*/ +/* // draw draws the `Item` and/or any child nodes. pub fn (ea &EventArea) draw(ui &UI) { ea.Item.draw(ui) } +*/ + +/* +// rect returns this `Item`'s rectangle. +pub fn (ea &EventArea) visual_state() VisualState { + return ea.Item.visual_state() +} +*/ +/* // event sends an `Event` any child nodes and/or it's own listeners. pub fn (ea &EventArea) event(e Event) ?&Node { return ea.Item.event(e) } +*/ @[heap] pub struct PointerEventArea { @@ -127,6 +190,7 @@ pub struct PointerEventArea { on_pointer_event []OnPointerEventFn } +/* // parent returns the parent Node. pub fn (pea &PointerEventArea) parent() &Node { return pea.EventArea.parent() @@ -137,6 +201,11 @@ pub fn (pea &PointerEventArea) draw(ui &UI) { pea.EventArea.draw(ui) } +// rect returns this `Item`'s rectangle. +pub fn (pea &PointerEventArea) visual_state() VisualState { + return pea.EventArea.visual_state() +}*/ + // event sends an `Event` any child nodes and/or it's own listeners. pub fn (pea &PointerEventArea) event(e Event) ?&Node { if e is MouseButtonEvent || e is MouseMotionEvent || e is MouseWheelEvent { diff --git a/ui/node.v b/ui/node.v index 95e0f64..9ae0388 100644 --- a/ui/node.v +++ b/ui/node.v @@ -3,18 +3,40 @@ // that can be found in the LICENSE file. module ui -// import shy.lib as shy +import shy.lib as shy + +pub struct VisualState { + shy.Rect +} @[heap] pub interface Node { id u64 draw(ui &UI) event(e Event) ?&Node + visual_state() VisualState mut: parent &Node body []&Node } +/* +// parent returns this node's parent `&Node`. +pub fn (n &Node) parent() &Node { + assert n != unsafe { nil } + // TODO not possible currently: if isnil(n.parent) { return none } + return n.parent +} +*/ + +// reparent sets `parent` to `new_parent` on this `&Node`. +pub fn (n &Node) reparent(new_parent &Node) { + assert n != unsafe { nil } + unsafe { + n.parent = new_parent + } +} + // collect collects all `Node`s matching `filter() == true` into `nodes`. pub fn (n &Node) collect(mut nodes []&Node, filter fn (n &Node) bool) { assert n != unsafe { nil } @@ -50,10 +72,3 @@ pub fn (n &Node) visit(func fn (n &Node)) { node.visit(func) } } - -// parent returns this node's parent `&Node`. -pub fn (n &Node) parent() &Node { - assert n != unsafe { nil } - // TODO not possible currently: if isnil(n.parent) { return none } - return n.parent -}