mirror of
https://github.com/danbulant/cushy
synced 2026-06-08 00:50:34 +00:00
Hover updates after widget removal
This also fixes some inconsistencies that arose when the focus widget was "stuck" on a removed widget. Button previously handled it hackily in a redraw function, but now Gooey handles it automatically without needing to wait for a repaint.
This commit is contained in:
parent
5506a24dae
commit
42840b950c
5 changed files with 154 additions and 120 deletions
|
|
@ -141,9 +141,14 @@ fn game_end(winner: Option<Player>, app: &Dynamic<AppState>) -> impl MakeWidget
|
||||||
};
|
};
|
||||||
|
|
||||||
label
|
label
|
||||||
.and("Play Again".into_button().on_click(move |_| {
|
.and(
|
||||||
app.set(AppState::Playing);
|
"Play Again"
|
||||||
}))
|
.into_button()
|
||||||
|
.on_click(move |_| {
|
||||||
|
app.set(AppState::Playing);
|
||||||
|
})
|
||||||
|
.into_default(),
|
||||||
|
)
|
||||||
.into_rows()
|
.into_rows()
|
||||||
.centered()
|
.centered()
|
||||||
.expand()
|
.expand()
|
||||||
|
|
|
||||||
112
src/context.rs
112
src/context.rs
|
|
@ -21,7 +21,7 @@ use crate::utils::IgnorePoison;
|
||||||
use crate::value::{Dynamic, IntoValue, Value};
|
use crate::value::{Dynamic, IntoValue, Value};
|
||||||
use crate::widget::{EventHandling, ManagedWidget, WidgetId, WidgetInstance, WidgetRef};
|
use crate::widget::{EventHandling, ManagedWidget, WidgetId, WidgetInstance, WidgetRef};
|
||||||
use crate::window::sealed::WindowCommand;
|
use crate::window::sealed::WindowCommand;
|
||||||
use crate::window::{RunningWindow, ThemeMode};
|
use crate::window::{CursorState, RunningWindow, ThemeMode};
|
||||||
use crate::ConstraintLimit;
|
use crate::ConstraintLimit;
|
||||||
|
|
||||||
/// A context to an event function.
|
/// A context to an event function.
|
||||||
|
|
@ -185,7 +185,10 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
|
|
||||||
let mut activation_changes = 0;
|
let mut activation_changes = 0;
|
||||||
while activation_changes < MAX_ITERS {
|
while activation_changes < MAX_ITERS {
|
||||||
let active = self.pending_state.active.clone();
|
let active = self
|
||||||
|
.pending_state
|
||||||
|
.active
|
||||||
|
.and_then(|w| self.current_node.tree.widget(w));
|
||||||
if self.current_node.tree.active_widget() == active.as_ref().map(|w| w.node_id) {
|
if self.current_node.tree.active_widget() == active.as_ref().map(|w| w.node_id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -202,13 +205,16 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
Err(()) => false,
|
Err(()) => false,
|
||||||
};
|
};
|
||||||
if new {
|
if new {
|
||||||
if let Some(active) = self.pending_state.active.clone() {
|
let active = self
|
||||||
|
.pending_state
|
||||||
|
.active
|
||||||
|
.and_then(|w| self.current_node.tree.widget(w));
|
||||||
|
if let Some(active) = &active {
|
||||||
active
|
active
|
||||||
.lock()
|
.lock()
|
||||||
.as_widget()
|
.as_widget()
|
||||||
.activate(&mut self.for_other(&active));
|
.activate(&mut self.for_other(active));
|
||||||
}
|
}
|
||||||
self.pending_state.active = active;
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -222,7 +228,10 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
|
|
||||||
let mut focus_changes = 0;
|
let mut focus_changes = 0;
|
||||||
while focus_changes < MAX_ITERS {
|
while focus_changes < MAX_ITERS {
|
||||||
let focus = self.pending_state.focus.clone();
|
let focus = self
|
||||||
|
.pending_state
|
||||||
|
.focus
|
||||||
|
.and_then(|w| self.current_node.tree.widget(w));
|
||||||
if self.current_node.tree.focused_widget() == focus.as_ref().map(|w| w.node_id) {
|
if self.current_node.tree.focused_widget() == focus.as_ref().map(|w| w.node_id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -235,7 +244,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
drop(focus_context);
|
drop(focus_context);
|
||||||
|
|
||||||
if accept_focus {
|
if accept_focus {
|
||||||
break Some(focus);
|
break Some(focus.id());
|
||||||
} else if let Some(next_focus) =
|
} else if let Some(next_focus) =
|
||||||
focus.explicit_focus_target(self.pending_state.focus_is_advancing)
|
focus.explicit_focus_target(self.pending_state.focus_is_advancing)
|
||||||
{
|
{
|
||||||
|
|
@ -244,11 +253,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
break self.next_focus_after(focus, self.pending_state.focus_is_advancing);
|
break self.next_focus_after(focus, self.pending_state.focus_is_advancing);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let new = match self
|
let new = match self.current_node.tree.focus(self.pending_state.focus) {
|
||||||
.current_node
|
|
||||||
.tree
|
|
||||||
.focus(self.pending_state.focus.as_ref())
|
|
||||||
{
|
|
||||||
Ok(old) => {
|
Ok(old) => {
|
||||||
if let Some(old) = old {
|
if let Some(old) = old {
|
||||||
let mut old_context = self.for_other(&old);
|
let mut old_context = self.for_other(&old);
|
||||||
|
|
@ -259,7 +264,11 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
Err(()) => false,
|
Err(()) => false,
|
||||||
};
|
};
|
||||||
if new {
|
if new {
|
||||||
if let Some(focus) = self.pending_state.focus.clone() {
|
if let Some(focus) = self
|
||||||
|
.pending_state
|
||||||
|
.focus
|
||||||
|
.and_then(|w| self.current_node.tree.widget(w))
|
||||||
|
{
|
||||||
focus.lock().as_widget().focus(&mut self.for_other(&focus));
|
focus.lock().as_widget().focus(&mut self.for_other(&focus));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -270,13 +279,40 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
if focus_changes == MAX_ITERS {
|
if focus_changes == MAX_ITERS {
|
||||||
tracing::error!("focus change force stopped after {focus_changes} sequential changes");
|
tracing::error!("focus change force stopped after {focus_changes} sequential changes");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that our hover widget still exists. If not, we should try to find a new one.
|
||||||
|
if let Some(hover) = self.current_node.tree.hovered_widget() {
|
||||||
|
if self.current_node.tree.widget_from_node(hover).is_none() {
|
||||||
|
self.update_hovered_widget();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_focus_after(
|
pub(crate) fn update_hovered_widget(&mut self) {
|
||||||
&mut self,
|
self.cursor.widget = None;
|
||||||
mut focus: ManagedWidget,
|
if let Some(location) = self.cursor.location {
|
||||||
advance: bool,
|
for widget in self.current_node.tree.widgets_under_point(location) {
|
||||||
) -> Option<ManagedWidget> {
|
let mut widget_context = self.for_other(&widget);
|
||||||
|
let Some(widget_layout) = widget_context.last_layout() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let relative = location - widget_layout.origin;
|
||||||
|
|
||||||
|
if widget_context.hit_test(relative) {
|
||||||
|
widget_context.hover(relative);
|
||||||
|
drop(widget_context);
|
||||||
|
self.cursor.widget = Some(widget.id());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.cursor.widget.is_none() {
|
||||||
|
self.clear_hover();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_focus_after(&mut self, mut focus: ManagedWidget, advance: bool) -> Option<WidgetId> {
|
||||||
// First, look within the current focus for any focusable children.
|
// First, look within the current focus for any focusable children.
|
||||||
let stop_at = focus.id();
|
let stop_at = focus.id();
|
||||||
if let Some(focus) = self.next_focus_within(&focus, None, stop_at, advance) {
|
if let Some(focus) = self.next_focus_within(&focus, None, stop_at, advance) {
|
||||||
|
|
@ -304,7 +340,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
focus: &ManagedWidget,
|
focus: &ManagedWidget,
|
||||||
stop_at: WidgetId,
|
stop_at: WidgetId,
|
||||||
advance: bool,
|
advance: bool,
|
||||||
) -> Option<ManagedWidget> {
|
) -> Option<WidgetId> {
|
||||||
self.next_focus_within(&focus.parent()?, Some(focus.id()), stop_at, advance)
|
self.next_focus_within(&focus.parent()?, Some(focus.id()), stop_at, advance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -317,7 +353,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
start_at: Option<WidgetId>,
|
start_at: Option<WidgetId>,
|
||||||
stop_at: WidgetId,
|
stop_at: WidgetId,
|
||||||
advance: bool,
|
advance: bool,
|
||||||
) -> Option<ManagedWidget> {
|
) -> Option<WidgetId> {
|
||||||
let mut visual_order = self.get(&LayoutOrder);
|
let mut visual_order = self.get(&LayoutOrder);
|
||||||
if !advance {
|
if !advance {
|
||||||
visual_order = visual_order.rev();
|
visual_order = visual_order.rev();
|
||||||
|
|
@ -346,9 +382,9 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
.as_widget()
|
.as_widget()
|
||||||
.accept_focus(&mut self.for_other(&child))
|
.accept_focus(&mut self.for_other(&child))
|
||||||
{
|
{
|
||||||
return Some(child);
|
return Some(child.id());
|
||||||
} else if let Some(next_focus) = self.widget().explicit_focus_target(advance) {
|
} else if let Some(next_focus) = self.widget().explicit_focus_target(advance) {
|
||||||
return Some(next_focus);
|
return Some(next_focus.id());
|
||||||
} else if let Some(focus) = self.next_focus_within(&child, None, stop_at, advance) {
|
} else if let Some(focus) = self.next_focus_within(&child, None, stop_at, advance) {
|
||||||
return Some(focus);
|
return Some(focus);
|
||||||
}
|
}
|
||||||
|
|
@ -705,6 +741,7 @@ pub struct WidgetContext<'context, 'window> {
|
||||||
redraw_status: &'context InvalidationStatus,
|
redraw_status: &'context InvalidationStatus,
|
||||||
window: &'context mut RunningWindow<'window>,
|
window: &'context mut RunningWindow<'window>,
|
||||||
theme: Cow<'context, ThemePair>,
|
theme: Cow<'context, ThemePair>,
|
||||||
|
cursor: &'context mut CursorState,
|
||||||
pending_state: PendingState<'context>,
|
pending_state: PendingState<'context>,
|
||||||
effective_styles: Styles,
|
effective_styles: Styles,
|
||||||
cache: WidgetCacheKey,
|
cache: WidgetCacheKey,
|
||||||
|
|
@ -717,6 +754,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
theme: &'context ThemePair,
|
theme: &'context ThemePair,
|
||||||
window: &'context mut RunningWindow<'window>,
|
window: &'context mut RunningWindow<'window>,
|
||||||
theme_mode: ThemeMode,
|
theme_mode: ThemeMode,
|
||||||
|
cursor: &'context mut CursorState,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let enabled = current_node.enabled(&WindowHandle {
|
let enabled = current_node.enabled(&WindowHandle {
|
||||||
kludgine: window.handle(),
|
kludgine: window.handle(),
|
||||||
|
|
@ -727,11 +765,11 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
focus: current_node
|
focus: current_node
|
||||||
.tree
|
.tree
|
||||||
.focused_widget()
|
.focused_widget()
|
||||||
.and_then(|id| current_node.tree.widget_from_node(id)),
|
.and_then(|id| current_node.tree.widget_from_node(id).map(|w| w.id())),
|
||||||
active: current_node
|
active: current_node
|
||||||
.tree
|
.tree
|
||||||
.active_widget()
|
.active_widget()
|
||||||
.and_then(|id| current_node.tree.widget_from_node(id)),
|
.and_then(|id| current_node.tree.widget_from_node(id).map(|w| w.id())),
|
||||||
focus_is_advancing: false,
|
focus_is_advancing: false,
|
||||||
}),
|
}),
|
||||||
effective_styles: current_node.effective_styles(),
|
effective_styles: current_node.effective_styles(),
|
||||||
|
|
@ -740,6 +778,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
enabled,
|
enabled,
|
||||||
invalidation: current_node.invalidation(),
|
invalidation: current_node.invalidation(),
|
||||||
},
|
},
|
||||||
|
cursor,
|
||||||
current_node,
|
current_node,
|
||||||
redraw_status,
|
redraw_status,
|
||||||
theme: Cow::Borrowed(theme),
|
theme: Cow::Borrowed(theme),
|
||||||
|
|
@ -757,6 +796,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
pending_state: self.pending_state.borrowed(),
|
pending_state: self.pending_state.borrowed(),
|
||||||
cache: self.cache,
|
cache: self.cache,
|
||||||
effective_styles: self.effective_styles.clone(),
|
effective_styles: self.effective_styles.clone(),
|
||||||
|
cursor: &mut *self.cursor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -793,6 +833,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
window: &mut *self.window,
|
window: &mut *self.window,
|
||||||
theme,
|
theme,
|
||||||
pending_state: self.pending_state.borrowed(),
|
pending_state: self.pending_state.borrowed(),
|
||||||
|
cursor: &mut *self.cursor,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -829,7 +870,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
/// contexts for the currently firing event are dropped.
|
/// contexts for the currently firing event are dropped.
|
||||||
pub fn focus(&mut self) {
|
pub fn focus(&mut self) {
|
||||||
self.pending_state.focus_is_advancing = true;
|
self.pending_state.focus_is_advancing = true;
|
||||||
self.pending_state.focus = Some(self.current_node.clone());
|
self.pending_state.focus = Some(self.current_node.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clear_focus(&mut self) {
|
pub(crate) fn clear_focus(&mut self) {
|
||||||
|
|
@ -859,16 +900,11 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
/// Widget events relating to activation changes are deferred until after
|
/// Widget events relating to activation changes are deferred until after
|
||||||
/// the all contexts for the currently firing event are dropped.
|
/// the all contexts for the currently firing event are dropped.
|
||||||
pub fn activate(&mut self) -> bool {
|
pub fn activate(&mut self) -> bool {
|
||||||
if self
|
if self.pending_state.active == Some(self.current_node.id()) {
|
||||||
.pending_state
|
|
||||||
.active
|
|
||||||
.as_ref()
|
|
||||||
.map_or(true, |active| active != &self.current_node)
|
|
||||||
{
|
|
||||||
self.pending_state.active = Some(self.current_node.clone());
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
false
|
||||||
|
} else {
|
||||||
|
self.pending_state.active = Some(self.current_node.id());
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -895,7 +931,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
/// Returns true if this widget is currently the active widget.
|
/// Returns true if this widget is currently the active widget.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn active(&self) -> bool {
|
pub fn active(&self) -> bool {
|
||||||
self.pending_state.active.as_ref() == Some(&self.current_node)
|
self.pending_state.active == Some(self.current_node.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this widget is currently hovered, even if the cursor is
|
/// Returns true if this widget is currently hovered, even if the cursor is
|
||||||
|
|
@ -914,7 +950,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
/// Returns true if this widget is currently focused for user input.
|
/// Returns true if this widget is currently focused for user input.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn focused(&self) -> bool {
|
pub fn focused(&self) -> bool {
|
||||||
self.pending_state.focus.as_ref() == Some(&self.current_node)
|
self.pending_state.focus == Some(self.current_node.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this widget is the target to activate when the user
|
/// Returns true if this widget is the target to activate when the user
|
||||||
|
|
@ -1100,8 +1136,8 @@ enum PendingState<'a> {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct PendingWidgetState {
|
struct PendingWidgetState {
|
||||||
focus_is_advancing: bool,
|
focus_is_advancing: bool,
|
||||||
focus: Option<ManagedWidget>,
|
focus: Option<WidgetId>,
|
||||||
active: Option<ManagedWidget>,
|
active: Option<WidgetId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PendingState<'_> {
|
impl PendingState<'_> {
|
||||||
|
|
|
||||||
42
src/tree.rs
42
src/tree.rs
|
|
@ -231,24 +231,27 @@ impl Tree {
|
||||||
let hovered = new_hover
|
let hovered = new_hover
|
||||||
.map(|new_hover| data.widget_hierarchy(new_hover.node_id, self))
|
.map(|new_hover| data.widget_hierarchy(new_hover.node_id, self))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let unhovered = match data.update_tracked_widget(new_hover, self, |data| &mut data.hover) {
|
let unhovered =
|
||||||
Ok(Some(old_hover)) => {
|
match data.update_tracked_widget(new_hover.map(ManagedWidget::id), self, |data| {
|
||||||
let mut old_hovered = data.widget_hierarchy(old_hover.node_id, self);
|
&mut data.hover
|
||||||
// For any widgets that were shared, remove them, as they don't
|
}) {
|
||||||
// need to have their events fired again.
|
Ok(Some(old_hover)) => {
|
||||||
let mut new_index = 0;
|
let mut old_hovered = data.widget_hierarchy(old_hover.node_id, self);
|
||||||
while !old_hovered.is_empty() && old_hovered.get(0) == hovered.get(new_index) {
|
// For any widgets that were shared, remove them, as they don't
|
||||||
old_hovered.remove(0);
|
// need to have their events fired again.
|
||||||
new_index += 1;
|
let mut new_index = 0;
|
||||||
|
while !old_hovered.is_empty() && old_hovered.get(0) == hovered.get(new_index) {
|
||||||
|
old_hovered.remove(0);
|
||||||
|
new_index += 1;
|
||||||
|
}
|
||||||
|
old_hovered
|
||||||
}
|
}
|
||||||
old_hovered
|
_ => Vec::new(),
|
||||||
}
|
};
|
||||||
_ => Vec::new(),
|
|
||||||
};
|
|
||||||
HoverResults { unhovered, hovered }
|
HoverResults { unhovered, hovered }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focus(&self, new_focus: Option<&ManagedWidget>) -> Result<Option<ManagedWidget>, ()> {
|
pub fn focus(&self, new_focus: Option<WidgetId>) -> Result<Option<ManagedWidget>, ()> {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock().ignore_poison();
|
||||||
data.update_tracked_widget(new_focus, self, |data| &mut data.focus)
|
data.update_tracked_widget(new_focus, self, |data| &mut data.focus)
|
||||||
}
|
}
|
||||||
|
|
@ -264,7 +267,9 @@ impl Tree {
|
||||||
new_active: Option<&ManagedWidget>,
|
new_active: Option<&ManagedWidget>,
|
||||||
) -> Result<Option<ManagedWidget>, ()> {
|
) -> Result<Option<ManagedWidget>, ()> {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock().ignore_poison();
|
||||||
data.update_tracked_widget(new_active, self, |data| &mut data.active)
|
data.update_tracked_widget(new_active.map(ManagedWidget::id), self, |data| {
|
||||||
|
&mut data.active
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn widget(&self, id: WidgetId) -> Option<ManagedWidget> {
|
pub fn widget(&self, id: WidgetId) -> Option<ManagedWidget> {
|
||||||
|
|
@ -334,7 +339,7 @@ impl Tree {
|
||||||
self.data.lock().ignore_poison().focus
|
self.data.lock().ignore_poison().focus
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn widgets_at_point(&self, point: Point<Px>) -> Vec<ManagedWidget> {
|
pub(crate) fn widgets_under_point(&self, point: Point<Px>) -> Vec<ManagedWidget> {
|
||||||
let data = self.data.lock().ignore_poison();
|
let data = self.data.lock().ignore_poison();
|
||||||
data.render_info.widgets_under_point(point, &data, self)
|
data.render_info.widgets_under_point(point, &data, self)
|
||||||
}
|
}
|
||||||
|
|
@ -489,12 +494,13 @@ impl TreeData {
|
||||||
|
|
||||||
fn update_tracked_widget(
|
fn update_tracked_widget(
|
||||||
&mut self,
|
&mut self,
|
||||||
new_widget: Option<&ManagedWidget>,
|
new_widget: Option<WidgetId>,
|
||||||
tree: &Tree,
|
tree: &Tree,
|
||||||
property: impl FnOnce(&mut Self) -> &mut Option<LotId>,
|
property: impl FnOnce(&mut Self) -> &mut Option<LotId>,
|
||||||
) -> Result<Option<ManagedWidget>, ()> {
|
) -> Result<Option<ManagedWidget>, ()> {
|
||||||
|
let new_widget = new_widget.and_then(|w| self.widget_from_id(w, tree));
|
||||||
match (
|
match (
|
||||||
mem::replace(property(self), new_widget.map(|w| w.node_id)),
|
mem::replace(property(self), new_widget.as_ref().map(|w| w.node_id)),
|
||||||
new_widget,
|
new_widget,
|
||||||
) {
|
) {
|
||||||
(Some(old_widget), Some(new_widget)) if old_widget == new_widget.node_id => Err(()),
|
(Some(old_widget), Some(new_widget)) if old_widget == new_widget.node_id => Err(()),
|
||||||
|
|
|
||||||
|
|
@ -219,11 +219,6 @@ impl Button {
|
||||||
kind,
|
kind,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO this should be genericized to happen automatically.
|
|
||||||
if !context.enabled() {
|
|
||||||
context.blur();
|
|
||||||
}
|
|
||||||
|
|
||||||
if context.is_default() {
|
if context.is_default() {
|
||||||
kind.colors_for_default(visual_state, context)
|
kind.colors_for_default(visual_state, context)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
104
src/window.rs
104
src/window.rs
|
|
@ -278,7 +278,8 @@ struct GooeyWindow<T> {
|
||||||
root: ManagedWidget,
|
root: ManagedWidget,
|
||||||
contents: Drawing,
|
contents: Drawing,
|
||||||
should_close: bool,
|
should_close: bool,
|
||||||
mouse_state: MouseState,
|
cursor: CursorState,
|
||||||
|
mouse_buttons: AHashMap<DeviceId, AHashMap<MouseButton, WidgetId>>,
|
||||||
redraw_status: InvalidationStatus,
|
redraw_status: InvalidationStatus,
|
||||||
initial_frame: bool,
|
initial_frame: bool,
|
||||||
occluded: Dynamic<bool>,
|
occluded: Dynamic<bool>,
|
||||||
|
|
@ -319,6 +320,7 @@ where
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
window,
|
window,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
)
|
)
|
||||||
|
|
@ -331,6 +333,7 @@ where
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
window,
|
window,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
)
|
)
|
||||||
|
|
@ -345,6 +348,7 @@ where
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
window,
|
window,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
)
|
)
|
||||||
|
|
@ -466,11 +470,11 @@ where
|
||||||
root,
|
root,
|
||||||
contents: Drawing::default(),
|
contents: Drawing::default(),
|
||||||
should_close: false,
|
should_close: false,
|
||||||
mouse_state: MouseState {
|
cursor: CursorState {
|
||||||
location: None,
|
location: None,
|
||||||
widget: None,
|
widget: None,
|
||||||
devices: AHashMap::default(),
|
|
||||||
},
|
},
|
||||||
|
mouse_buttons: AHashMap::default(),
|
||||||
redraw_status: InvalidationStatus::default(),
|
redraw_status: InvalidationStatus::default(),
|
||||||
initial_frame: true,
|
initial_frame: true,
|
||||||
occluded,
|
occluded,
|
||||||
|
|
@ -517,6 +521,7 @@ where
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
gfx: Exclusive::Owned(Graphics::new(graphics)),
|
gfx: Exclusive::Owned(Graphics::new(graphics)),
|
||||||
};
|
};
|
||||||
|
|
@ -684,11 +689,9 @@ where
|
||||||
is_synthetic: bool,
|
is_synthetic: bool,
|
||||||
) {
|
) {
|
||||||
let target = self.root.tree.focused_widget().unwrap_or(self.root.node_id);
|
let target = self.root.tree.focused_widget().unwrap_or(self.root.node_id);
|
||||||
let target = self
|
let Some(target) = self.root.tree.widget_from_node(target) else {
|
||||||
.root
|
return;
|
||||||
.tree
|
};
|
||||||
.widget_from_node(target)
|
|
||||||
.expect("missing widget");
|
|
||||||
let mut window = RunningWindow::new(window, &self.focused, &self.occluded);
|
let mut window = RunningWindow::new(window, &self.focused, &self.occluded);
|
||||||
let mut target = EventContext::new(
|
let mut target = EventContext::new(
|
||||||
WidgetContext::new(
|
WidgetContext::new(
|
||||||
|
|
@ -697,6 +700,7 @@ where
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -731,6 +735,7 @@ where
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -798,6 +803,7 @@ where
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -833,6 +839,7 @@ where
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -849,10 +856,24 @@ where
|
||||||
position: PhysicalPosition<f64>,
|
position: PhysicalPosition<f64>,
|
||||||
) {
|
) {
|
||||||
let location = Point::<Px>::from(position);
|
let location = Point::<Px>::from(position);
|
||||||
self.mouse_state.location = Some(location);
|
self.cursor.location = Some(location);
|
||||||
|
|
||||||
let mut window = RunningWindow::new(window, &self.focused, &self.occluded);
|
let mut window = RunningWindow::new(window, &self.focused, &self.occluded);
|
||||||
if let Some(state) = self.mouse_state.devices.get(&device_id) {
|
|
||||||
|
EventContext::new(
|
||||||
|
WidgetContext::new(
|
||||||
|
self.root.clone(),
|
||||||
|
&self.redraw_status,
|
||||||
|
&self.current_theme,
|
||||||
|
&mut window,
|
||||||
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
|
),
|
||||||
|
kludgine,
|
||||||
|
)
|
||||||
|
.update_hovered_widget();
|
||||||
|
|
||||||
|
if let Some(state) = self.mouse_buttons.get(&device_id) {
|
||||||
// Mouse Drag
|
// Mouse Drag
|
||||||
for (button, handler) in state {
|
for (button, handler) in state {
|
||||||
let Some(handler) = self.root.tree.widget(*handler) else {
|
let Some(handler) = self.root.tree.widget(*handler) else {
|
||||||
|
|
@ -865,6 +886,7 @@ where
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -873,37 +895,6 @@ where
|
||||||
};
|
};
|
||||||
context.mouse_drag(location - last_rendered_at.origin, device_id, *button);
|
context.mouse_drag(location - last_rendered_at.origin, device_id, *button);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Hover
|
|
||||||
let mut context = EventContext::new(
|
|
||||||
WidgetContext::new(
|
|
||||||
self.root.clone(),
|
|
||||||
&self.redraw_status,
|
|
||||||
&self.current_theme,
|
|
||||||
&mut window,
|
|
||||||
self.theme_mode.get(),
|
|
||||||
),
|
|
||||||
kludgine,
|
|
||||||
);
|
|
||||||
self.mouse_state.widget = None;
|
|
||||||
for widget in self.root.tree.widgets_at_point(location) {
|
|
||||||
let mut widget_context = context.for_other(&widget);
|
|
||||||
let Some(widget_layout) = widget_context.last_layout() else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let relative = location - widget_layout.origin;
|
|
||||||
|
|
||||||
if widget_context.hit_test(relative) {
|
|
||||||
widget_context.hover(relative);
|
|
||||||
drop(widget_context);
|
|
||||||
self.mouse_state.widget = Some(widget.id());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.mouse_state.widget.is_none() {
|
|
||||||
context.clear_hover();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -913,7 +904,7 @@ where
|
||||||
kludgine: &mut Kludgine,
|
kludgine: &mut Kludgine,
|
||||||
_device_id: DeviceId,
|
_device_id: DeviceId,
|
||||||
) {
|
) {
|
||||||
if self.mouse_state.widget.take().is_some() {
|
if self.cursor.widget.take().is_some() {
|
||||||
let mut window = RunningWindow::new(window, &self.focused, &self.occluded);
|
let mut window = RunningWindow::new(window, &self.focused, &self.occluded);
|
||||||
let mut context = EventContext::new(
|
let mut context = EventContext::new(
|
||||||
WidgetContext::new(
|
WidgetContext::new(
|
||||||
|
|
@ -922,6 +913,7 @@ where
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -947,6 +939,7 @@ where
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
)
|
)
|
||||||
|
|
@ -954,10 +947,8 @@ where
|
||||||
|
|
||||||
if let (ElementState::Pressed, Some(location), Some(hovered)) = (
|
if let (ElementState::Pressed, Some(location), Some(hovered)) = (
|
||||||
state,
|
state,
|
||||||
&self.mouse_state.location,
|
self.cursor.location,
|
||||||
self.mouse_state
|
self.cursor.widget.and_then(|id| self.root.tree.widget(id)),
|
||||||
.widget
|
|
||||||
.and_then(|id| self.root.tree.widget(id)),
|
|
||||||
) {
|
) {
|
||||||
if let Some(handler) = recursively_handle_event(
|
if let Some(handler) = recursively_handle_event(
|
||||||
&mut EventContext::new(
|
&mut EventContext::new(
|
||||||
|
|
@ -967,6 +958,7 @@ where
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
),
|
),
|
||||||
|
|
@ -974,12 +966,11 @@ where
|
||||||
let Some(layout) = context.last_layout() else {
|
let Some(layout) = context.last_layout() else {
|
||||||
return IGNORED;
|
return IGNORED;
|
||||||
};
|
};
|
||||||
let relative = *location - layout.origin;
|
let relative = location - layout.origin;
|
||||||
context.mouse_down(relative, device_id, button)
|
context.mouse_down(relative, device_id, button)
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
self.mouse_state
|
self.mouse_buttons
|
||||||
.devices
|
|
||||||
.entry(device_id)
|
.entry(device_id)
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(button, handler.id());
|
.insert(button, handler.id());
|
||||||
|
|
@ -987,18 +978,19 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ElementState::Released => {
|
ElementState::Released => {
|
||||||
let Some(device_buttons) = self.mouse_state.devices.get_mut(&device_id) else {
|
let Some(device_buttons) = self.mouse_buttons.get_mut(&device_id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(handler) = device_buttons.remove(&button) else {
|
let Some(handler) = device_buttons.remove(&button) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if device_buttons.is_empty() {
|
if device_buttons.is_empty() {
|
||||||
self.mouse_state.devices.remove(&device_id);
|
self.mouse_buttons.remove(&device_id);
|
||||||
}
|
}
|
||||||
let Some(handler) = self.root.tree.widget(handler) else {
|
let Some(handler) = self.root.tree.widget(handler) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
let cursor_location = self.cursor.location;
|
||||||
let mut context = EventContext::new(
|
let mut context = EventContext::new(
|
||||||
WidgetContext::new(
|
WidgetContext::new(
|
||||||
handler,
|
handler,
|
||||||
|
|
@ -1006,12 +998,13 @@ where
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
||||||
let relative = if let (Some(last_rendered), Some(location)) =
|
let relative = if let (Some(last_rendered), Some(location)) =
|
||||||
(context.last_layout(), self.mouse_state.location)
|
(context.last_layout(), cursor_location)
|
||||||
{
|
{
|
||||||
Some(location - last_rendered.origin)
|
Some(location - last_rendered.origin)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1060,10 +1053,9 @@ fn recursively_handle_event(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct MouseState {
|
pub(crate) struct CursorState {
|
||||||
location: Option<Point<Px>>,
|
pub(crate) location: Option<Point<Px>>,
|
||||||
widget: Option<WidgetId>,
|
pub(crate) widget: Option<WidgetId>,
|
||||||
devices: AHashMap<DeviceId, AHashMap<MouseButton, WidgetId>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue