Initial window focus

This commit is contained in:
Jonathan Johnson 2023-11-06 06:19:41 -08:00
parent 0f6d3838b1
commit fbf6134a0a
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
10 changed files with 184 additions and 92 deletions

View file

@ -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()
} }

View file

@ -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,

View file

@ -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))
}
}

View file

@ -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,
{ {

View file

@ -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());
} }
} }

View file

@ -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>,

View file

@ -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>,

View file

@ -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
}) })
} }
} }

View file

@ -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();

View file

@ -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,
); );