mirror of
https://github.com/danbulant/cushy
synced 2026-07-06 11:40:37 +00:00
Initial window focus
This commit is contained in:
parent
0f6d3838b1
commit
fbf6134a0a
10 changed files with 184 additions and 92 deletions
|
|
@ -28,6 +28,7 @@ fn main() -> gooey::Result {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.make_widget();
|
.make_widget();
|
||||||
|
let input_id = input.id();
|
||||||
|
|
||||||
Expand::new(Stack::rows(children![
|
Expand::new(Stack::rows(children![
|
||||||
Expand::new(Stack::columns(children![
|
Expand::new(Stack::columns(children![
|
||||||
|
|
@ -47,6 +48,6 @@ fn main() -> gooey::Result {
|
||||||
])),
|
])),
|
||||||
input.clone(),
|
input.clone(),
|
||||||
]))
|
]))
|
||||||
.with_next_focus(input)
|
.with_next_focus(input_id)
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
|
|
||||||
pub(crate) fn apply_pending_state(&mut self) {
|
pub(crate) fn apply_pending_state(&mut self) {
|
||||||
let active = self.pending_state.active.clone();
|
let active = self.pending_state.active.clone();
|
||||||
if self.current_node.tree.active_widget() != active.as_ref().map(|active| active.id) {
|
if self.current_node.tree.active_widget() != active.as_ref().map(ManagedWidget::id) {
|
||||||
let new = match self.current_node.tree.activate(active.as_ref()) {
|
let new = match self.current_node.tree.activate(active.as_ref()) {
|
||||||
Ok(old) => {
|
Ok(old) => {
|
||||||
if let Some(old) = old {
|
if let Some(old) = old {
|
||||||
|
|
@ -178,17 +178,32 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
};
|
};
|
||||||
if new {
|
if new {
|
||||||
if let Some(active) = active {
|
if let Some(active) = &active {
|
||||||
active
|
active
|
||||||
.lock()
|
.lock()
|
||||||
.as_widget()
|
.as_widget()
|
||||||
.activate(&mut self.for_other(active.clone()));
|
.activate(&mut self.for_other(active.clone()));
|
||||||
}
|
}
|
||||||
|
self.pending_state.active = active;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let focus = self.pending_state.focus.clone();
|
let focus = self.pending_state.focus.clone();
|
||||||
if self.current_node.tree.focused_widget() != focus.as_ref().map(|focus| focus.id) {
|
if self.current_node.tree.focused_widget() != focus.as_ref().map(ManagedWidget::id) {
|
||||||
|
let focus = focus.and_then(|mut focus| loop {
|
||||||
|
if focus
|
||||||
|
.lock()
|
||||||
|
.as_widget()
|
||||||
|
.accept_focus(&mut self.for_other(focus.clone()))
|
||||||
|
{
|
||||||
|
break Some(focus);
|
||||||
|
} else if let Some(next_focus) = focus.next_focus() {
|
||||||
|
focus = next_focus;
|
||||||
|
} else {
|
||||||
|
// TODO visually scan the tree for the "next" widget.
|
||||||
|
break None;
|
||||||
|
}
|
||||||
|
});
|
||||||
let new = match self.current_node.tree.focus(focus.as_ref()) {
|
let new = match self.current_node.tree.focus(focus.as_ref()) {
|
||||||
Ok(old) => {
|
Ok(old) => {
|
||||||
if let Some(old) = old {
|
if let Some(old) = old {
|
||||||
|
|
@ -200,12 +215,13 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
};
|
};
|
||||||
if new {
|
if new {
|
||||||
if let Some(focus) = focus {
|
if let Some(focus) = &focus {
|
||||||
focus
|
focus
|
||||||
.lock()
|
.lock()
|
||||||
.as_widget()
|
.as_widget()
|
||||||
.focus(&mut self.for_other(focus.clone()));
|
.focus(&mut self.for_other(focus.clone()));
|
||||||
}
|
}
|
||||||
|
self.pending_state.focus = focus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -324,6 +340,14 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for GraphicsContext<'_, '_, '_, '_, '_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if matches!(self.widget.pending_state, PendingState::Owned(_)) {
|
||||||
|
self.as_event_context().apply_pending_state();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'context, 'window, 'clip, 'gfx, 'pass> Deref
|
impl<'context, 'window, 'clip, 'gfx, 'pass> Deref
|
||||||
for GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>
|
for GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>
|
||||||
{
|
{
|
||||||
|
|
@ -497,11 +521,11 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
focus: current_node
|
focus: current_node
|
||||||
.tree
|
.tree
|
||||||
.focused_widget()
|
.focused_widget()
|
||||||
.map(|id| current_node.tree.widget(id)),
|
.and_then(|id| current_node.tree.widget(id)),
|
||||||
active: current_node
|
active: current_node
|
||||||
.tree
|
.tree
|
||||||
.active_widget()
|
.active_widget()
|
||||||
.map(|id| current_node.tree.widget(id)),
|
.and_then(|id| current_node.tree.widget(id)),
|
||||||
}),
|
}),
|
||||||
current_node,
|
current_node,
|
||||||
redraw_status,
|
redraw_status,
|
||||||
|
|
|
||||||
113
src/tree.rs
113
src/tree.rs
|
|
@ -1,8 +1,9 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::sync::atomic::{self, AtomicU64};
|
||||||
use std::sync::{Arc, Mutex, PoisonError};
|
use std::sync::{Arc, Mutex, PoisonError};
|
||||||
|
|
||||||
use alot::{LotId, Lots};
|
|
||||||
use kludgine::figures::units::Px;
|
use kludgine::figures::units::Px;
|
||||||
use kludgine::figures::{Point, Rect};
|
use kludgine::figures::{Point, Rect};
|
||||||
|
|
||||||
|
|
@ -21,19 +22,22 @@ impl Tree {
|
||||||
parent: Option<&ManagedWidget>,
|
parent: Option<&ManagedWidget>,
|
||||||
) -> ManagedWidget {
|
) -> ManagedWidget {
|
||||||
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
let id = WidgetId(data.nodes.push(Node {
|
let id = widget.id();
|
||||||
widget: widget.clone(),
|
data.nodes.insert(
|
||||||
children: Vec::new(),
|
id,
|
||||||
parent: parent.map(|parent| parent.id),
|
Node {
|
||||||
layout: None,
|
widget: widget.clone(),
|
||||||
styles: None,
|
children: Vec::new(),
|
||||||
}));
|
parent: parent.map(ManagedWidget::id),
|
||||||
|
layout: None,
|
||||||
|
styles: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
if let Some(parent) = parent {
|
if let Some(parent) = parent {
|
||||||
let parent = &mut data.nodes[parent.id.0];
|
let parent = data.nodes.get_mut(&parent.id()).expect("missing parent");
|
||||||
parent.children.push(id);
|
parent.children.push(id);
|
||||||
}
|
}
|
||||||
ManagedWidget {
|
ManagedWidget {
|
||||||
id,
|
|
||||||
widget,
|
widget,
|
||||||
tree: self.clone(),
|
tree: self.clone(),
|
||||||
}
|
}
|
||||||
|
|
@ -41,26 +45,31 @@ impl Tree {
|
||||||
|
|
||||||
pub fn remove_child(&self, child: &ManagedWidget, parent: &ManagedWidget) {
|
pub fn remove_child(&self, child: &ManagedWidget, parent: &ManagedWidget) {
|
||||||
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
data.remove_child(child.id, parent.id);
|
data.remove_child(child.id(), parent.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_layout(&self, widget: WidgetId, rect: Rect<Px>) {
|
pub(crate) fn set_layout(&self, widget: WidgetId, rect: Rect<Px>) {
|
||||||
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
rect.extents();
|
|
||||||
data.nodes[widget.0].layout = Some(rect);
|
|
||||||
data.render_order.push(widget);
|
data.render_order.push(widget);
|
||||||
let mut children_to_offset = data.nodes[widget.0].children.clone();
|
let node = data.nodes.get_mut(&widget).expect("missing widget");
|
||||||
|
node.layout = Some(rect);
|
||||||
|
let mut children_to_offset = node.children.clone();
|
||||||
while let Some(child) = children_to_offset.pop() {
|
while let Some(child) = children_to_offset.pop() {
|
||||||
if let Some(layout) = &mut data.nodes[child.0].layout {
|
if let Some(layout) = data
|
||||||
|
.nodes
|
||||||
|
.get_mut(&child)
|
||||||
|
.and_then(|child| child.layout.as_mut())
|
||||||
|
{
|
||||||
layout.origin += rect.origin;
|
layout.origin += rect.origin;
|
||||||
children_to_offset.extend(data.nodes[child.0].children.iter().copied());
|
children_to_offset.extend(data.nodes[&child].children.iter().copied());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn layout(&self, widget: WidgetId) -> Option<Rect<Px>> {
|
pub(crate) fn layout(&self, widget: WidgetId) -> Option<Rect<Px>> {
|
||||||
let data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
let data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
data.nodes[widget.0].layout
|
data.nodes[&widget].layout
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn reset_render_order(&self) {
|
pub(crate) fn reset_render_order(&self) {
|
||||||
|
|
@ -70,20 +79,20 @@ impl Tree {
|
||||||
|
|
||||||
pub(crate) fn reset_child_layouts(&self, parent: WidgetId) {
|
pub(crate) fn reset_child_layouts(&self, parent: WidgetId) {
|
||||||
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
let children = data.nodes[parent.0].children.clone();
|
let children = data.nodes[&parent].children.clone();
|
||||||
for child in children {
|
for child in children {
|
||||||
data.nodes[child.0].layout = None;
|
data.nodes.get_mut(&child).expect("missing widget").layout = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn hover(&self, new_hover: Option<&ManagedWidget>) -> HoverResults {
|
pub(crate) fn hover(&self, new_hover: Option<&ManagedWidget>) -> HoverResults {
|
||||||
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
let hovered = new_hover
|
let hovered = new_hover
|
||||||
.map(|new_hover| data.widget_hierarchy(new_hover.id, self))
|
.map(|new_hover| data.widget_hierarchy(new_hover.id(), self))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let unhovered = match data.update_tracked_widget(new_hover, self, |data| &mut data.hover) {
|
let unhovered = match data.update_tracked_widget(new_hover, self, |data| &mut data.hover) {
|
||||||
Ok(Some(old_hover)) => {
|
Ok(Some(old_hover)) => {
|
||||||
let mut old_hovered = data.widget_hierarchy(old_hover.id, self);
|
let mut old_hovered = data.widget_hierarchy(old_hover.id(), self);
|
||||||
// For any widgets that were shared, remove them, as they don't
|
// For any widgets that were shared, remove them, as they don't
|
||||||
// need to have their events fired again.
|
// need to have their events fired again.
|
||||||
let mut new_index = 0;
|
let mut new_index = 0;
|
||||||
|
|
@ -111,7 +120,7 @@ impl Tree {
|
||||||
data.update_tracked_widget(new_active, self, |data| &mut data.active)
|
data.update_tracked_widget(new_active, self, |data| &mut data.active)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn widget(&self, id: WidgetId) -> ManagedWidget {
|
pub fn widget(&self, id: WidgetId) -> Option<ManagedWidget> {
|
||||||
let data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
let data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
data.widget(id, self)
|
data.widget(id, self)
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +146,7 @@ impl Tree {
|
||||||
if hovered == id {
|
if hovered == id {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
search = data.nodes[hovered.0].parent;
|
search = data.nodes[&hovered].parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
|
|
@ -154,11 +163,10 @@ impl Tree {
|
||||||
let data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
let data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
let mut hits = Vec::new();
|
let mut hits = Vec::new();
|
||||||
for id in data.render_order.iter().rev() {
|
for id in data.render_order.iter().rev() {
|
||||||
if let Some(last_rendered) = data.nodes[id.0].layout {
|
if let Some(last_rendered) = data.nodes[id].layout {
|
||||||
if last_rendered.contains(point) {
|
if last_rendered.contains(point) {
|
||||||
hits.push(ManagedWidget {
|
hits.push(ManagedWidget {
|
||||||
id: *id,
|
widget: data.nodes[id].widget.clone(),
|
||||||
widget: data.nodes[id.0].widget.clone(),
|
|
||||||
tree: self.clone(),
|
tree: self.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -169,12 +177,12 @@ impl Tree {
|
||||||
|
|
||||||
pub(crate) fn parent(&self, id: WidgetId) -> Option<WidgetId> {
|
pub(crate) fn parent(&self, id: WidgetId) -> Option<WidgetId> {
|
||||||
let data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
let data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
data.nodes[id.0].parent
|
data.nodes.get(&id).expect("missing widget").parent
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn attach_styles(&self, id: WidgetId, styles: Styles) {
|
pub(crate) fn attach_styles(&self, id: WidgetId, styles: Styles) {
|
||||||
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
data.nodes[id.0].styles = Some(styles);
|
data.nodes.get_mut(&id).expect("missing widget").styles = Some(styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query_styles(
|
pub fn query_styles(
|
||||||
|
|
@ -185,7 +193,7 @@ impl Tree {
|
||||||
self.data
|
self.data
|
||||||
.lock()
|
.lock()
|
||||||
.map_or_else(PoisonError::into_inner, |g| g)
|
.map_or_else(PoisonError::into_inner, |g| g)
|
||||||
.query_styles(perspective.id, query)
|
.query_styles(perspective.id(), query)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query_style<Component: ComponentDefinition>(
|
pub fn query_style<Component: ComponentDefinition>(
|
||||||
|
|
@ -196,7 +204,7 @@ impl Tree {
|
||||||
self.data
|
self.data
|
||||||
.lock()
|
.lock()
|
||||||
.map_or_else(PoisonError::into_inner, |g| g)
|
.map_or_else(PoisonError::into_inner, |g| g)
|
||||||
.query_style(perspective.id, component)
|
.query_style(perspective.id(), component)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,7 +215,7 @@ pub(crate) struct HoverResults {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct TreeData {
|
struct TreeData {
|
||||||
nodes: Lots<Node>,
|
nodes: HashMap<WidgetId, Node>,
|
||||||
active: Option<WidgetId>,
|
active: Option<WidgetId>,
|
||||||
focus: Option<WidgetId>,
|
focus: Option<WidgetId>,
|
||||||
hover: Option<WidgetId>,
|
hover: Option<WidgetId>,
|
||||||
|
|
@ -215,17 +223,16 @@ struct TreeData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TreeData {
|
impl TreeData {
|
||||||
fn widget(&self, id: WidgetId, tree: &Tree) -> ManagedWidget {
|
fn widget(&self, id: WidgetId, tree: &Tree) -> Option<ManagedWidget> {
|
||||||
ManagedWidget {
|
Some(ManagedWidget {
|
||||||
id,
|
widget: self.nodes.get(&id)?.widget.clone(),
|
||||||
widget: self.nodes[id.0].widget.clone(),
|
|
||||||
tree: tree.clone(),
|
tree: tree.clone(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_child(&mut self, child: WidgetId, parent: WidgetId) {
|
fn remove_child(&mut self, child: WidgetId, parent: WidgetId) {
|
||||||
let removed_node = self.nodes.remove(child.0).expect("widget already removed");
|
let removed_node = self.nodes.remove(&child).expect("widget already removed");
|
||||||
let parent = &mut self.nodes[parent.0];
|
let parent = self.nodes.get_mut(&parent).expect("missing widget");
|
||||||
let index = parent
|
let index = parent
|
||||||
.children
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -236,16 +243,16 @@ impl TreeData {
|
||||||
let mut detached_nodes = removed_node.children;
|
let mut detached_nodes = removed_node.children;
|
||||||
|
|
||||||
while let Some(node) = detached_nodes.pop() {
|
while let Some(node) = detached_nodes.pop() {
|
||||||
let mut node = self.nodes.remove(node.0).expect("detached node missing");
|
let mut node = self.nodes.remove(&node).expect("detached node missing");
|
||||||
detached_nodes.append(&mut node.children);
|
detached_nodes.append(&mut node.children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn widget_hierarchy(&self, mut widget: WidgetId, tree: &Tree) -> Vec<ManagedWidget> {
|
pub(crate) fn widget_hierarchy(&self, mut widget: WidgetId, tree: &Tree) -> Vec<ManagedWidget> {
|
||||||
let mut hierarchy = Vec::new();
|
let mut hierarchy = Vec::new();
|
||||||
loop {
|
while let Some(managed) = self.widget(widget, tree) {
|
||||||
hierarchy.push(self.widget(widget, tree));
|
hierarchy.push(managed);
|
||||||
let Some(parent) = self.nodes[widget.0].parent else {
|
let Some(parent) = self.nodes[&widget].parent else {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
widget = parent;
|
widget = parent;
|
||||||
|
|
@ -263,13 +270,12 @@ impl TreeData {
|
||||||
property: impl FnOnce(&mut Self) -> &mut Option<WidgetId>,
|
property: impl FnOnce(&mut Self) -> &mut Option<WidgetId>,
|
||||||
) -> Result<Option<ManagedWidget>, ()> {
|
) -> Result<Option<ManagedWidget>, ()> {
|
||||||
match (
|
match (
|
||||||
mem::replace(property(self), new_widget.map(|w| w.id)),
|
mem::replace(property(self), new_widget.map(ManagedWidget::id)),
|
||||||
new_widget,
|
new_widget,
|
||||||
) {
|
) {
|
||||||
(Some(old_widget), Some(new_widget)) if old_widget == new_widget.id => Err(()),
|
(Some(old_widget), Some(new_widget)) if old_widget == new_widget.id() => Err(()),
|
||||||
(Some(old_widget), _) => Ok(Some(ManagedWidget {
|
(Some(old_widget), _) => Ok(Some(ManagedWidget {
|
||||||
id: old_widget,
|
widget: self.nodes[&old_widget].widget.clone(),
|
||||||
widget: self.nodes[old_widget.0].widget.clone(),
|
|
||||||
tree: tree.clone(),
|
tree: tree.clone(),
|
||||||
})),
|
})),
|
||||||
(None, _) => Ok(None),
|
(None, _) => Ok(None),
|
||||||
|
|
@ -284,7 +290,7 @@ impl TreeData {
|
||||||
let mut query = query.iter().map(|n| n.name()).collect::<Vec<_>>();
|
let mut query = query.iter().map(|n| n.name()).collect::<Vec<_>>();
|
||||||
let mut resolved = Styles::new();
|
let mut resolved = Styles::new();
|
||||||
while !query.is_empty() {
|
while !query.is_empty() {
|
||||||
let node = &self.nodes[perspective.0];
|
let node = &self.nodes[&perspective];
|
||||||
if let Some(styles) = &node.styles {
|
if let Some(styles) = &node.styles {
|
||||||
query.retain(|name| {
|
query.retain(|name| {
|
||||||
if let Some(component) = styles.get(name) {
|
if let Some(component) = styles.get(name) {
|
||||||
|
|
@ -308,7 +314,7 @@ impl TreeData {
|
||||||
) -> Component::ComponentType {
|
) -> Component::ComponentType {
|
||||||
let name = query.name();
|
let name = query.name();
|
||||||
loop {
|
loop {
|
||||||
let node = &self.nodes[perspective.0];
|
let node = &self.nodes[&perspective];
|
||||||
if let Some(styles) = &node.styles {
|
if let Some(styles) = &node.styles {
|
||||||
if let Some(component) = styles.get(&name) {
|
if let Some(component) = styles.get(&name) {
|
||||||
let Ok(value) =
|
let Ok(value) =
|
||||||
|
|
@ -334,5 +340,12 @@ pub struct Node {
|
||||||
pub styles: Option<Styles>,
|
pub styles: Option<Styles>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
|
||||||
pub struct WidgetId(LotId);
|
pub struct WidgetId(u64);
|
||||||
|
|
||||||
|
impl WidgetId {
|
||||||
|
pub fn unique() -> Self {
|
||||||
|
static COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||||
|
Self(COUNTER.fetch_add(1, atomic::Ordering::Acquire))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -479,7 +479,7 @@ impl<T> Value<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a clone of the currently stored value.
|
/// Returns a clone of the currently stored value.
|
||||||
pub fn get(&mut self) -> T
|
pub fn get(&self) -> T
|
||||||
where
|
where
|
||||||
T: Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ pub trait MakeWidget: Sized {
|
||||||
///
|
///
|
||||||
/// Gooey automatically determines reverse tab order by using this same
|
/// Gooey automatically determines reverse tab order by using this same
|
||||||
/// relationship.
|
/// relationship.
|
||||||
fn with_next_focus(self, next_focus: impl IntoValue<Option<WidgetInstance>>) -> WidgetInstance {
|
fn with_next_focus(self, next_focus: impl IntoValue<Option<WidgetId>>) -> WidgetInstance {
|
||||||
self.make_widget().with_next_focus(next_focus)
|
self.make_widget().with_next_focus(next_focus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -242,8 +242,9 @@ where
|
||||||
/// An instance of a [`Widget`].
|
/// An instance of a [`Widget`].
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct WidgetInstance {
|
pub struct WidgetInstance {
|
||||||
|
id: WidgetId,
|
||||||
widget: Arc<Mutex<dyn AnyWidget>>,
|
widget: Arc<Mutex<dyn AnyWidget>>,
|
||||||
next_focus: Value<Option<Arc<Mutex<dyn AnyWidget>>>>,
|
next_focus: Value<Option<WidgetId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WidgetInstance {
|
impl WidgetInstance {
|
||||||
|
|
@ -253,11 +254,18 @@ impl WidgetInstance {
|
||||||
W: Widget,
|
W: Widget,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
|
id: WidgetId::unique(),
|
||||||
widget: Arc::new(Mutex::new(widget)),
|
widget: Arc::new(Mutex::new(widget)),
|
||||||
next_focus: Value::default(),
|
next_focus: Value::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the unique id of this widget instance.
|
||||||
|
#[must_use]
|
||||||
|
pub fn id(&self) -> WidgetId {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the widget that should be focused next.
|
/// Sets the widget that should be focused next.
|
||||||
///
|
///
|
||||||
/// Gooey automatically determines reverse tab order by using this same
|
/// Gooey automatically determines reverse tab order by using this same
|
||||||
|
|
@ -265,17 +273,9 @@ impl WidgetInstance {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_next_focus(
|
pub fn with_next_focus(
|
||||||
mut self,
|
mut self,
|
||||||
next_focus: impl IntoValue<Option<WidgetInstance>>,
|
next_focus: impl IntoValue<Option<WidgetId>>,
|
||||||
) -> WidgetInstance {
|
) -> WidgetInstance {
|
||||||
self.next_focus = match next_focus.into_value() {
|
self.next_focus = next_focus.into_value();
|
||||||
Value::Constant(maybe_widget) => {
|
|
||||||
Value::Constant(maybe_widget.map(|widget| widget.widget))
|
|
||||||
}
|
|
||||||
Value::Dynamic(dynamic) => Value::Dynamic(
|
|
||||||
dynamic
|
|
||||||
.map_each(|instance| instance.as_ref().map(|instance| instance.widget.clone())),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,7 +361,6 @@ where
|
||||||
/// A [`Widget`] that has been attached to a widget hierarchy.
|
/// A [`Widget`] that has been attached to a widget hierarchy.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ManagedWidget {
|
pub struct ManagedWidget {
|
||||||
pub(crate) id: WidgetId,
|
|
||||||
pub(crate) widget: WidgetInstance,
|
pub(crate) widget: WidgetInstance,
|
||||||
pub(crate) tree: Tree,
|
pub(crate) tree: Tree,
|
||||||
}
|
}
|
||||||
|
|
@ -369,7 +368,6 @@ pub struct ManagedWidget {
|
||||||
impl Debug for ManagedWidget {
|
impl Debug for ManagedWidget {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("ManagedWidget")
|
f.debug_struct("ManagedWidget")
|
||||||
.field("id", &self.id)
|
|
||||||
.field("widget", &self.widget)
|
.field("widget", &self.widget)
|
||||||
.finish_non_exhaustive()
|
.finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
|
|
@ -385,51 +383,71 @@ impl ManagedWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_layout(&self, rect: Rect<Px>) {
|
pub(crate) fn set_layout(&self, rect: Rect<Px>) {
|
||||||
self.tree.set_layout(self.id, rect);
|
self.tree.set_layout(self.id(), rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the unique id of this widget instance.
|
||||||
|
#[must_use]
|
||||||
|
pub fn id(&self) -> WidgetId {
|
||||||
|
self.widget.id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the next widget to focus after this widget.
|
||||||
|
///
|
||||||
|
/// This function returns the value set in
|
||||||
|
/// [`MakeWidget::with_next_focus()`].
|
||||||
|
#[must_use]
|
||||||
|
pub fn next_focus(&self) -> Option<ManagedWidget> {
|
||||||
|
self.widget
|
||||||
|
.next_focus
|
||||||
|
.get()
|
||||||
|
.and_then(|next_focus| self.tree.widget(next_focus))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the region that the widget was last rendered at.
|
/// Returns the region that the widget was last rendered at.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn last_layout(&self) -> Option<Rect<Px>> {
|
pub fn last_layout(&self) -> Option<Rect<Px>> {
|
||||||
self.tree.layout(self.id)
|
self.tree.layout(self.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this widget is the currently active widget.
|
/// Returns true if this widget is the currently active widget.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn active(&self) -> bool {
|
pub fn active(&self) -> bool {
|
||||||
self.tree.active_widget() == Some(self.id)
|
self.tree.active_widget() == Some(self.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this widget is currently the hovered widget.
|
/// Returns true if this widget is currently the hovered widget.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn hovered(&self) -> bool {
|
pub fn hovered(&self) -> bool {
|
||||||
self.tree.is_hovered(self.id)
|
self.tree.is_hovered(self.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this widget that is directly beneath the cursor.
|
/// Returns true if this widget that is directly beneath the cursor.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn primary_hover(&self) -> bool {
|
pub fn primary_hover(&self) -> bool {
|
||||||
self.tree.hovered_widget() == Some(self.id)
|
self.tree.hovered_widget() == Some(self.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this widget is the currently focused widget.
|
/// Returns true if this widget is the currently focused widget.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn focused(&self) -> bool {
|
pub fn focused(&self) -> bool {
|
||||||
self.tree.focused_widget() == Some(self.id)
|
self.tree.focused_widget() == Some(self.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the parent of this widget.
|
/// Returns the parent of this widget.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn parent(&self) -> Option<ManagedWidget> {
|
pub fn parent(&self) -> Option<ManagedWidget> {
|
||||||
self.tree.parent(self.id).map(|id| self.tree.widget(id))
|
self.tree
|
||||||
|
.parent(self.id())
|
||||||
|
.and_then(|id| self.tree.widget(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn attach_styles(&self, styles: Styles) {
|
pub(crate) fn attach_styles(&self, styles: Styles) {
|
||||||
self.tree.attach_styles(self.id, styles);
|
self.tree.attach_styles(self.id(), styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn reset_child_layouts(&self) {
|
pub(crate) fn reset_child_layouts(&self) {
|
||||||
self.tree.reset_child_layouts(self.id);
|
self.tree.reset_child_layouts(self.id());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,11 @@ impl Widget for Button {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn accept_focus(&mut self, _context: &mut EventContext<'_, '_>) -> bool {
|
||||||
|
// TODO this should be driven by a "focus_all_widgets" setting that hopefully can be queried from the OS.
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn mouse_down(
|
fn mouse_down(
|
||||||
&mut self,
|
&mut self,
|
||||||
_location: Point<Px>,
|
_location: Point<Px>,
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,10 @@ impl Widget for Input {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn accept_focus(&mut self, _context: &mut EventContext<'_, '_>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn mouse_down(
|
fn mouse_down(
|
||||||
&mut self,
|
&mut self,
|
||||||
location: Point<Px>,
|
location: Point<Px>,
|
||||||
|
|
|
||||||
|
|
@ -65,11 +65,11 @@ impl Widget for Label {
|
||||||
self.text.map(|contents| {
|
self.text.map(|contents| {
|
||||||
let measured = context
|
let measured = context
|
||||||
.graphics
|
.graphics
|
||||||
.measure_text(Text::from(contents).wrap_at(dbg!(width)));
|
.measure_text(Text::from(contents).wrap_at(width));
|
||||||
let mut size = measured.size.try_cast().unwrap_or_default();
|
let mut size = measured.size.try_cast().unwrap_or_default();
|
||||||
size += padding * 2;
|
size += padding * 2;
|
||||||
self.prepared_text = Some(measured);
|
self.prepared_text = Some(measured);
|
||||||
dbg!(size)
|
size
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ impl Stack {
|
||||||
direction: impl IntoValue<StackDirection>,
|
direction: impl IntoValue<StackDirection>,
|
||||||
widgets: impl IntoValue<Children>,
|
widgets: impl IntoValue<Children>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut direction = direction.into_value();
|
let direction = direction.into_value();
|
||||||
|
|
||||||
let initial_direction = direction.get();
|
let initial_direction = direction.get();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ use kludgine::render::Drawing;
|
||||||
use kludgine::Kludgine;
|
use kludgine::Kludgine;
|
||||||
|
|
||||||
use crate::context::{
|
use crate::context::{
|
||||||
EventContext, Exclusive, GraphicsContext, LayoutContext, RedrawStatus, WidgetContext,
|
AsEventContext, EventContext, Exclusive, GraphicsContext, LayoutContext, RedrawStatus,
|
||||||
|
WidgetContext,
|
||||||
};
|
};
|
||||||
use crate::graphics::Graphics;
|
use crate::graphics::Graphics;
|
||||||
use crate::tree::Tree;
|
use crate::tree::Tree;
|
||||||
|
|
@ -154,6 +155,7 @@ struct GooeyWindow<T> {
|
||||||
should_close: bool,
|
should_close: bool,
|
||||||
mouse_state: MouseState,
|
mouse_state: MouseState,
|
||||||
redraw_status: RedrawStatus,
|
redraw_status: RedrawStatus,
|
||||||
|
initial_frame: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> GooeyWindow<T>
|
impl<T> GooeyWindow<T>
|
||||||
|
|
@ -192,6 +194,7 @@ where
|
||||||
devices: HashMap::default(),
|
devices: HashMap::default(),
|
||||||
},
|
},
|
||||||
redraw_status: RedrawStatus::default(),
|
redraw_status: RedrawStatus::default(),
|
||||||
|
initial_frame: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,6 +216,12 @@ where
|
||||||
let render_size = actual_size.min(window_size);
|
let render_size = actual_size.min(window_size);
|
||||||
self.root.set_layout(Rect::from(render_size.into_signed()));
|
self.root.set_layout(Rect::from(render_size.into_signed()));
|
||||||
|
|
||||||
|
if self.initial_frame {
|
||||||
|
self.initial_frame = false;
|
||||||
|
layout_context.focus();
|
||||||
|
layout_context.as_event_context().apply_pending_state();
|
||||||
|
}
|
||||||
|
|
||||||
if render_size.width < window_size.width || render_size.height < window_size.height {
|
if render_size.width < window_size.width || render_size.height < window_size.height {
|
||||||
layout_context
|
layout_context
|
||||||
.clipped_to(Rect::from(render_size.into_signed()))
|
.clipped_to(Rect::from(render_size.into_signed()))
|
||||||
|
|
@ -285,8 +294,8 @@ where
|
||||||
input: KeyEvent,
|
input: KeyEvent,
|
||||||
is_synthetic: bool,
|
is_synthetic: bool,
|
||||||
) {
|
) {
|
||||||
let target = self.root.tree.focused_widget().unwrap_or(self.root.id);
|
let target = self.root.tree.focused_widget().unwrap_or(self.root.id());
|
||||||
let target = self.root.tree.widget(target);
|
let target = self.root.tree.widget(target).expect("missing widget");
|
||||||
let mut target = EventContext::new(
|
let mut target = EventContext::new(
|
||||||
WidgetContext::new(target, &self.redraw_status, &mut window),
|
WidgetContext::new(target, &self.redraw_status, &mut window),
|
||||||
kludgine,
|
kludgine,
|
||||||
|
|
@ -320,9 +329,18 @@ where
|
||||||
delta: MouseScrollDelta,
|
delta: MouseScrollDelta,
|
||||||
phase: TouchPhase,
|
phase: TouchPhase,
|
||||||
) {
|
) {
|
||||||
let widget = self.root.tree.hovered_widget().unwrap_or(self.root.id);
|
let widget = self
|
||||||
|
.root
|
||||||
|
.tree
|
||||||
|
.hovered_widget()
|
||||||
|
.and_then(|hovered| self.root.tree.widget(hovered))
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
self.root
|
||||||
|
.tree
|
||||||
|
.widget(self.root.id())
|
||||||
|
.expect("missing widget")
|
||||||
|
});
|
||||||
|
|
||||||
let widget = self.root.tree.widget(widget);
|
|
||||||
let mut widget = EventContext::new(
|
let mut widget = EventContext::new(
|
||||||
WidgetContext::new(widget, &self.redraw_status, &mut window),
|
WidgetContext::new(widget, &self.redraw_status, &mut window),
|
||||||
kludgine,
|
kludgine,
|
||||||
|
|
@ -335,10 +353,19 @@ where
|
||||||
// fn modifiers_changed(&mut self, window: kludgine::app::Window<'_, ()>) {}
|
// fn modifiers_changed(&mut self, window: kludgine::app::Window<'_, ()>) {}
|
||||||
|
|
||||||
fn ime(&mut self, mut window: RunningWindow<'_>, kludgine: &mut Kludgine, ime: Ime) {
|
fn ime(&mut self, mut window: RunningWindow<'_>, kludgine: &mut Kludgine, ime: Ime) {
|
||||||
let target = self.root.tree.focused_widget().unwrap_or(self.root.id);
|
let widget = self
|
||||||
let target = self.root.tree.widget(target);
|
.root
|
||||||
|
.tree
|
||||||
|
.focused_widget()
|
||||||
|
.and_then(|hovered| self.root.tree.widget(hovered))
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
self.root
|
||||||
|
.tree
|
||||||
|
.widget(self.root.id())
|
||||||
|
.expect("missing widget")
|
||||||
|
});
|
||||||
let mut target = EventContext::new(
|
let mut target = EventContext::new(
|
||||||
WidgetContext::new(target, &self.redraw_status, &mut window),
|
WidgetContext::new(widget, &self.redraw_status, &mut window),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue