Widget docs + refactoring

- MakeWidgetWithId::make_with_id -> MakeWidgetWithTag::make_With_tag
- ManagedWidget -> MountedWidget
- *_refresh -> *_redraw, standardized on terminology
- get_tracked -> get_tracking_redraw
This commit is contained in:
Jonathan Johnson 2023-12-17 07:38:31 -08:00
parent 01e04a4eb9
commit 02d6b343f1
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
25 changed files with 376 additions and 199 deletions

22
Cargo.lock generated
View file

@ -1181,13 +1181,13 @@ dependencies = [
[[package]]
name = "lyon_tessellation"
version = "1.0.12"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f5bcf02928361d18e6edb8ad3bc5b93cba8aa57e2508deb072c2d2ade8bbd0d"
checksum = "8c7c67b5bc8123b352b2e7e742b47d1f236a13fe77619433be9568fbd888e9c0"
dependencies = [
"float_next_after",
"lyon_path",
"thiserror",
"num-traits",
]
[[package]]
@ -1271,9 +1271,9 @@ dependencies = [
[[package]]
name = "memmap2"
version = "0.9.0"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375"
checksum = "39a69c7c189ae418f83003da62820aca28d15a07725ce51fb924999335d622ff"
dependencies = [
"libc",
]
@ -2064,7 +2064,7 @@ dependencies = [
"cursor-icon",
"libc",
"log",
"memmap2 0.9.0",
"memmap2 0.9.2",
"rustix",
"thiserror",
"wayland-backend",
@ -2164,18 +2164,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.50"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.50"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
dependencies = [
"proc-macro2",
"quote",
@ -2982,7 +2982,7 @@ dependencies = [
"js-sys",
"libc",
"log",
"memmap2 0.9.0",
"memmap2 0.9.2",
"ndk",
"ndk-sys",
"objc2",

View file

@ -2,7 +2,7 @@
//! traits or using the [`Custom`] widget with callbacks.
use gooey::value::Dynamic;
use gooey::widget::{MakeWidget, MakeWidgetWithId, Widget, WidgetInstance, WidgetTag, HANDLED};
use gooey::widget::{MakeWidget, MakeWidgetWithTag, Widget, WidgetInstance, WidgetTag, HANDLED};
use gooey::widgets::Custom;
use gooey::Run;
use kludgine::figures::units::{Lp, UPx};
@ -29,7 +29,7 @@ fn main() -> gooey::Result {
///
/// This approach was added to make it easy to create one-off widgets in a
/// hierarchy to handle events or other purpose-built functions.
fn callback_widget() -> impl MakeWidgetWithId {
fn callback_widget() -> impl MakeWidgetWithTag {
// This implementation and the impl `Widget` implementation both use the
// same Dynamic value setup.
let toggle = Toggle::default();
@ -44,7 +44,7 @@ fn callback_widget() -> impl MakeWidgetWithId {
.height(Lp::inches(1))
}
/// A second approach is to implement [`MakeWidgetWithId`] for a type. This
/// A second approach is to implement [`MakeWidgetWithTag`] for a type. This
/// allows any type to be used when composing your UI that know how to create a
/// widget.
///
@ -52,7 +52,7 @@ fn callback_widget() -> impl MakeWidgetWithId {
/// widgets) in a reusable fashion.
///
/// [`MakeWidget`] is implemented automatically for all types that implement
/// [`MakeWidgetWithId`]. The difference between the traits is purely whether
/// [`MakeWidgetWithTag`]. The difference between the traits is purely whether
/// allowing a caller instantiating your custom widget to provide an id for the
/// widget. These IDs are used when configuring custom tab orders, so if your
/// widget or any of its children aren't focusable, implementing [`MakeWidget`]
@ -60,10 +60,10 @@ fn callback_widget() -> impl MakeWidgetWithId {
#[derive(Default)]
struct ToggleMakeWidget(Toggle);
impl MakeWidgetWithId for ToggleMakeWidget {
fn make_with_id(self, id: WidgetTag) -> WidgetInstance {
impl MakeWidgetWithTag for ToggleMakeWidget {
fn make_with_tag(self, id: WidgetTag) -> WidgetInstance {
// In a real code base, the contents of callback_widget() would go here
callback_widget().make_with_id(id)
callback_widget().make_with_tag(id)
}
}
@ -71,7 +71,7 @@ impl MakeWidgetWithId for ToggleMakeWidget {
///
/// This is the lowest-level way to implement a Widget, but it also provides the
/// most power and flexibility.
fn impl_widget() -> impl MakeWidgetWithId {
fn impl_widget() -> impl MakeWidgetWithTag {
Toggle::default()
}
@ -91,7 +91,7 @@ impl Default for Toggle {
impl Widget for Toggle {
fn redraw(&mut self, context: &mut gooey::context::GraphicsContext<'_, '_, '_, '_, '_>) {
context.fill(self.color.get_tracking_refresh(context));
context.fill(self.color.get_tracking_redraw(context));
}
fn layout(

View file

@ -1,7 +1,7 @@
use std::process::exit;
use gooey::value::{Dynamic, MapEach};
use gooey::widget::{MakeWidget, MakeWidgetWithId, WidgetTag};
use gooey::widget::{MakeWidget, MakeWidgetWithTag, WidgetTag};
use gooey::widgets::grid::{Grid, GridDimension, GridWidgets};
use gooey::widgets::input::{InputValue, MaskedString};
use gooey::widgets::Expand;
@ -24,7 +24,7 @@ fn main() -> gooey::Result {
let username_row = (
"Username",
username.clone().into_input().make_with_id(username_tag),
username.clone().into_input().make_with_tag(username_tag),
);
let password_row = (
@ -38,7 +38,7 @@ fn main() -> gooey::Result {
eprintln!("Login cancelled");
exit(0)
})
.make_with_id(cancel_tag)
.make_with_tag(cancel_tag)
.into_escape()
.with_next_focus(username_id)
.and(Expand::empty())
@ -49,7 +49,7 @@ fn main() -> gooey::Result {
println!("Welcome, {}", username.get());
exit(0);
})
.make_with_id(login_tag)
.make_with_tag(login_tag)
.with_enabled(valid)
.into_default()
.with_next_focus(cancel_id),

View file

@ -1,6 +1,6 @@
use std::panic::UnwindSafe;
use gooey::widget::{MakeWidget, MakeWidgetWithId, WidgetTag};
use gooey::widget::{MakeWidget, MakeWidgetWithTag, WidgetTag};
use gooey::widgets::container::ContainerShadow;
use gooey::widgets::layers::{OverlayBuilder, OverlayLayer};
use gooey::Run;
@ -48,7 +48,7 @@ fn test_widget(overlay: &OverlayLayer, is_root: bool) -> impl MakeWidget {
);
}
buttons.pad().make_with_id(my_tag)
buttons.pad().make_with_tag(my_tag)
}
fn show_overlay_button(

View file

@ -25,7 +25,7 @@ use crate::styles::{ComponentDefinition, Styles, Theme, ThemePair};
use crate::utils::IgnorePoison;
use crate::value::{IntoValue, Value};
use crate::widget::{
EventHandling, ManagedWidget, RootBehavior, WidgetId, WidgetInstance, WidgetRef,
EventHandling, MountedWidget, RootBehavior, WidgetId, WidgetInstance, WidgetRef,
};
use crate::window::{CursorState, RunningWindow, ThemeMode};
use crate::ConstraintLimit;
@ -350,7 +350,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
}
}
fn next_focus_after(&mut self, mut focus: ManagedWidget, advance: bool) -> Option<WidgetId> {
fn next_focus_after(&mut self, mut focus: MountedWidget, advance: bool) -> Option<WidgetId> {
// First, look within the current focus for any focusable children.
let stop_at = focus.id();
if let Some(focus) = self.next_focus_within(&focus, None, stop_at, advance) {
@ -375,7 +375,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
fn next_focus_sibling(
&mut self,
focus: &ManagedWidget,
focus: &MountedWidget,
stop_at: WidgetId,
advance: bool,
) -> Option<WidgetId> {
@ -387,7 +387,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
/// that returns true from `accept_focus`.
fn next_focus_within(
&mut self,
focus: &ManagedWidget,
focus: &MountedWidget,
start_at: Option<WidgetId>,
stop_at: WidgetId,
advance: bool,
@ -782,7 +782,7 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> LayoutContext<'context, 'window, 'cl
/// Sets the layout for `child` to `layout`.
///
/// `layout` is relative to the current widget's controls.
pub fn set_child_layout(&mut self, child: &ManagedWidget, layout: Rect<Px>) {
pub fn set_child_layout(&mut self, child: &MountedWidget, layout: Rect<Px>) {
// TODO verify that `child` belongs to the current node.
if self.persist_layout {
child.set_layout(layout);
@ -824,7 +824,7 @@ pub trait AsEventContext<'window> {
/// Pushes a new child widget into the widget hierarchy beneathq the
/// context's widget.
#[must_use]
fn push_child(&mut self, child: WidgetInstance) -> ManagedWidget {
fn push_child(&mut self, child: WidgetInstance) -> MountedWidget {
let mut context = self.as_event_context();
let pushed_widget = context
.current_node
@ -838,7 +838,7 @@ pub trait AsEventContext<'window> {
}
/// Removes a widget from the hierarchy.
fn remove_child(&mut self, child: &ManagedWidget) {
fn remove_child(&mut self, child: &MountedWidget) {
let mut context = self.as_event_context();
child
.lock()
@ -868,7 +868,7 @@ impl<'window> AsEventContext<'window> for GraphicsContext<'_, 'window, '_, '_, '
/// This type provides access to the widget hierarchy from the perspective of a
/// specific widget.
pub struct WidgetContext<'context, 'window> {
current_node: ManagedWidget,
current_node: MountedWidget,
redraw_status: &'context InvalidationStatus,
window: &'context mut RunningWindow<'window>,
theme: Cow<'context, ThemePair>,
@ -880,7 +880,7 @@ pub struct WidgetContext<'context, 'window> {
impl<'context, 'window> WidgetContext<'context, 'window> {
pub(crate) fn new(
current_node: ManagedWidget,
current_node: MountedWidget,
redraw_status: &'context InvalidationStatus,
theme: &'context ThemePair,
window: &'context mut RunningWindow<'window>,
@ -942,12 +942,12 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
widget.manage(self).map(|current_node| {
let (effective_styles, theme, theme_mode) = current_node.overidden_theme();
let theme = if let Some(theme) = theme {
Cow::Owned(theme.get_tracked(self))
Cow::Owned(theme.get_tracking_redraw(self))
} else {
Cow::Borrowed(self.theme.as_ref())
};
let theme_mode = if let Some(mode) = theme_mode {
mode.get_tracked(self)
mode.get_tracking_redraw(self)
} else {
self.cache.theme_mode
};
@ -981,7 +981,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
self.cache.enabled
}
pub(crate) fn parent(&self) -> Option<ManagedWidget> {
pub(crate) fn parent(&self) -> Option<MountedWidget> {
self.current_node.parent()
}
@ -1096,7 +1096,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
#[must_use]
pub fn focused(&self, check_window: bool) -> bool {
self.pending_state.focus == Some(self.current_node.id())
&& (!check_window || self.window.focused().get_tracking_refresh(self))
&& (!check_window || self.window.focused().get_tracking_redraw(self))
}
/// Returns true if this widget is the target to activate when the user
@ -1123,7 +1123,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
/// Returns the widget this context is for.
#[must_use]
pub const fn widget(&self) -> &ManagedWidget {
pub const fn widget(&self) -> &MountedWidget {
&self.current_node
}
@ -1315,18 +1315,18 @@ impl InvalidationStatus {
}
}
/// A type chat can convert to a [`ManagedWidget`] through a [`WidgetContext`].
/// A type chat can convert to a [`MountedWidget`] through a [`WidgetContext`].
pub trait ManageWidget {
/// The managed type, which can be `Option<ManagedWidget>` or
/// `ManagedWidget`.
type Managed: MapManagedWidget<ManagedWidget>;
/// The managed type, which can be `Option<MountedWidget>` or
/// `MountedWidget`.
type Managed: MapManagedWidget<MountedWidget>;
/// Resolve `self` into a [`ManagedWidget`].
/// Resolve `self` into a [`MountedWidget`].
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed;
}
impl ManageWidget for WidgetId {
type Managed = Option<ManagedWidget>;
type Managed = Option<MountedWidget>;
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed {
context.current_node.tree.widget(*self)
@ -1334,7 +1334,7 @@ impl ManageWidget for WidgetId {
}
impl ManageWidget for WidgetInstance {
type Managed = Option<ManagedWidget>;
type Managed = Option<MountedWidget>;
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed {
context.current_node.tree.widget(self.id())
@ -1342,7 +1342,7 @@ impl ManageWidget for WidgetInstance {
}
impl ManageWidget for WidgetRef {
type Managed = Option<ManagedWidget>;
type Managed = Option<MountedWidget>;
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed {
match self {
@ -1352,7 +1352,7 @@ impl ManageWidget for WidgetRef {
}
}
impl ManageWidget for ManagedWidget {
impl ManageWidget for MountedWidget {
type Managed = Self;
fn manage(&self, _context: &WidgetContext<'_, '_>) -> Self::Managed {
@ -1360,27 +1360,27 @@ impl ManageWidget for ManagedWidget {
}
}
/// A type that can produce another type when provided a [`ManagedWidget`].
/// A type that can produce another type when provided a [`MountedWidget`].
pub trait MapManagedWidget<T> {
/// The result of the mapping operation.
type Result;
/// Call `map` with a [`ManagedWidget`].
fn map(self, map: impl FnOnce(ManagedWidget) -> T) -> Self::Result;
/// Call `map` with a [`MountedWidget`].
fn map(self, map: impl FnOnce(MountedWidget) -> T) -> Self::Result;
}
impl<T> MapManagedWidget<T> for Option<ManagedWidget> {
impl<T> MapManagedWidget<T> for Option<MountedWidget> {
type Result = Option<T>;
fn map(self, map: impl FnOnce(ManagedWidget) -> T) -> Self::Result {
fn map(self, map: impl FnOnce(MountedWidget) -> T) -> Self::Result {
self.map(map)
}
}
impl<T> MapManagedWidget<T> for ManagedWidget {
impl<T> MapManagedWidget<T> for MountedWidget {
type Result = T;
fn map(self, map: impl FnOnce(ManagedWidget) -> T) -> Self::Result {
fn map(self, map: impl FnOnce(MountedWidget) -> T) -> Self::Result {
map(self)
}
}

View file

@ -125,24 +125,6 @@ pub trait Run: Sized {
fn run(self) -> crate::Result;
}
/// Creates a [`Children`](crate::widget::Children) instance with the given list
/// of widgets.
#[macro_export]
#[deprecated = "use MakeWidget.and()/Children.and() to chain widgets without a macro"]
macro_rules! children {
() => {
$crate::widget::Children::new()
};
($($widget:expr),+) => {{
let mut widgets = $crate::widget::Children::with_capacity($crate::count!($($widget),+ ;));
$(widgets.push($widget);)+
widgets
}};
($($widget:expr),+ ,) => {{
$crate::children!($($widget),+)
}};
}
/// Counts the number of expressions passed to it.
///
/// This is used inside of Gooey macros to preallocate collections.

View file

@ -10,7 +10,7 @@ use crate::context::sealed::WindowHandle;
use crate::styles::{Styles, ThemePair, VisualOrder};
use crate::utils::IgnorePoison;
use crate::value::Value;
use crate::widget::{ManagedWidget, WidgetId, WidgetInstance};
use crate::widget::{MountedWidget, WidgetId, WidgetInstance};
use crate::window::ThemeMode;
use crate::ConstraintLimit;
@ -23,8 +23,8 @@ impl Tree {
pub fn push_boxed(
&self,
widget: WidgetInstance,
parent: Option<&ManagedWidget>,
) -> ManagedWidget {
parent: Option<&MountedWidget>,
) -> MountedWidget {
let mut data = self.data.lock().ignore_poison();
let id = widget.id();
let (effective_styles, parent_id) = if let Some(parent) = parent {
@ -60,14 +60,14 @@ impl Tree {
if let Some(next_focus) = widget.next_focus() {
data.previous_focuses.insert(next_focus, id);
}
ManagedWidget {
MountedWidget {
node_id,
widget,
tree: self.clone(),
}
}
pub fn remove_child(&self, child: &ManagedWidget, parent: &ManagedWidget) {
pub fn remove_child(&self, child: &MountedWidget, parent: &MountedWidget) {
let mut data = self.data.lock().ignore_poison();
data.remove_child(child.node_id, parent.node_id);
@ -163,11 +163,11 @@ impl Tree {
&self,
parent: LotId,
order: VisualOrder,
) -> Vec<ManagedWidget> {
) -> Vec<MountedWidget> {
let data = self.data.lock().ignore_poison();
let node = &data.nodes[parent];
let mut unordered = node.children.clone();
let mut ordered = Vec::<ManagedWidget>::with_capacity(unordered.len());
let mut ordered = Vec::<MountedWidget>::with_capacity(unordered.len());
loop {
// Identify the next "row" of widgets by finding the top of a widget that is the closest to the origin of
let mut min_vertical = order.vertical.max_px();
@ -225,13 +225,13 @@ impl Tree {
data.nodes[id].effective_styles.clone()
}
pub(crate) fn hover(&self, new_hover: Option<&ManagedWidget>) -> HoverResults {
pub(crate) fn hover(&self, new_hover: Option<&MountedWidget>) -> HoverResults {
let mut data = self.data.lock().ignore_poison();
let hovered = new_hover
.map(|new_hover| data.widget_hierarchy(new_hover.node_id, self))
.unwrap_or_default();
let unhovered =
match data.update_tracked_widget(new_hover.map(ManagedWidget::id), self, |data| {
match data.update_tracked_widget(new_hover.map(MountedWidget::id), self, |data| {
&mut data.hover
}) {
Ok(Some(old_hover)) => {
@ -250,12 +250,12 @@ impl Tree {
HoverResults { unhovered, hovered }
}
pub fn focus(&self, new_focus: Option<WidgetId>) -> Result<Option<ManagedWidget>, ()> {
pub fn focus(&self, new_focus: Option<WidgetId>) -> Result<Option<MountedWidget>, ()> {
let mut data = self.data.lock().ignore_poison();
data.update_tracked_widget(new_focus, self, |data| &mut data.focus)
}
pub fn previous_focus(&self, focus: WidgetId) -> Option<ManagedWidget> {
pub fn previous_focus(&self, focus: WidgetId) -> Option<MountedWidget> {
let data = self.data.lock().ignore_poison();
let previous = *data.previous_focuses.get(&focus)?;
data.widget_from_id(previous, self)
@ -263,20 +263,20 @@ impl Tree {
pub fn activate(
&self,
new_active: Option<&ManagedWidget>,
) -> Result<Option<ManagedWidget>, ()> {
new_active: Option<&MountedWidget>,
) -> Result<Option<MountedWidget>, ()> {
let mut data = self.data.lock().ignore_poison();
data.update_tracked_widget(new_active.map(ManagedWidget::id), self, |data| {
data.update_tracked_widget(new_active.map(MountedWidget::id), self, |data| {
&mut data.active
})
}
pub fn widget(&self, id: WidgetId) -> Option<ManagedWidget> {
pub fn widget(&self, id: WidgetId) -> Option<MountedWidget> {
let data = self.data.lock().ignore_poison();
data.widget_from_id(id, self)
}
pub(crate) fn widget_from_node(&self, id: LotId) -> Option<ManagedWidget> {
pub(crate) fn widget_from_node(&self, id: LotId) -> Option<MountedWidget> {
let data = self.data.lock().ignore_poison();
data.widget_from_node(id, self)
}
@ -333,7 +333,7 @@ impl Tree {
self.data.lock().ignore_poison().focus
}
pub(crate) fn widgets_under_point(&self, point: Point<Px>) -> Vec<ManagedWidget> {
pub(crate) fn widgets_under_point(&self, point: Point<Px>) -> Vec<MountedWidget> {
let data = self.data.lock().ignore_poison();
data.render_info.widgets_under_point(point, &data, self)
}
@ -398,8 +398,8 @@ impl Tree {
}
pub(crate) struct HoverResults {
pub unhovered: Vec<ManagedWidget>,
pub hovered: Vec<ManagedWidget>,
pub unhovered: Vec<MountedWidget>,
pub hovered: Vec<MountedWidget>,
}
#[derive(Default)]
@ -416,17 +416,17 @@ struct TreeData {
}
impl TreeData {
fn widget_from_id(&self, id: WidgetId, tree: &Tree) -> Option<ManagedWidget> {
fn widget_from_id(&self, id: WidgetId, tree: &Tree) -> Option<MountedWidget> {
let node_id = *self.nodes_by_id.get(&id)?;
Some(ManagedWidget {
Some(MountedWidget {
node_id,
widget: self.nodes[node_id].widget.clone(),
tree: tree.clone(),
})
}
fn widget_from_node(&self, node_id: LotId, tree: &Tree) -> Option<ManagedWidget> {
Some(ManagedWidget {
fn widget_from_node(&self, node_id: LotId, tree: &Tree) -> Option<MountedWidget> {
Some(MountedWidget {
node_id,
widget: self.nodes.get(node_id)?.widget.clone(),
tree: tree.clone(),
@ -489,7 +489,7 @@ impl TreeData {
}
}
pub(crate) fn widget_hierarchy(&self, mut widget: LotId, tree: &Tree) -> Vec<ManagedWidget> {
pub(crate) fn widget_hierarchy(&self, mut widget: LotId, tree: &Tree) -> Vec<MountedWidget> {
let mut hierarchy = Vec::new();
while let Some(managed) = self.widget_from_node(widget, tree) {
hierarchy.push(managed);
@ -509,7 +509,7 @@ impl TreeData {
new_widget: Option<WidgetId>,
tree: &Tree,
property: impl FnOnce(&mut Self) -> &mut Option<LotId>,
) -> Result<Option<ManagedWidget>, ()> {
) -> Result<Option<MountedWidget>, ()> {
let new_widget = new_widget.and_then(|w| self.widget_from_id(w, tree));
match (
mem::replace(property(self), new_widget.as_ref().map(|w| w.node_id)),
@ -555,7 +555,7 @@ impl RenderInfo {
point: Point<Px>,
tree_data: &TreeData,
tree: &Tree,
) -> Vec<ManagedWidget> {
) -> Vec<MountedWidget> {
// We pessimistically allocate a vector as if all widgets match, up to a
// reasonable limit. This should ensure minimal allocations in all but
// extreme circumstances where widgets are nested with a significant

View file

@ -22,7 +22,7 @@ use crate::animation::{AnimationHandle, DynamicTransition, IntoAnimate, LinearIn
use crate::context::sealed::WindowHandle;
use crate::context::{self, WidgetContext};
use crate::utils::{run_in_bg, IgnorePoison, UnwindsafeCondvar, WithClone};
use crate::widget::{Children, MakeWidget, MakeWidgetWithId, WidgetId, WidgetInstance};
use crate::widget::{Children, MakeWidget, MakeWidgetWithTag, WidgetId, WidgetInstance};
use crate::widgets::{Radio, Select, Space, Switcher};
/// An instance of a value that provides APIs to observe and react to its
@ -377,7 +377,7 @@ impl<T> Dynamic<T> {
/// This function panics if this value is already locked by the current
/// thread.
#[must_use]
pub fn get_tracking_refresh(&self, context: &WidgetContext<'_, '_>) -> T
pub fn get_tracking_redraw(&self, context: &WidgetContext<'_, '_>) -> T
where
T: Clone,
{
@ -638,20 +638,20 @@ impl Dynamic<WidgetInstance> {
}
}
impl MakeWidgetWithId for Dynamic<WidgetInstance> {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
self.into_switcher().make_with_id(id)
impl MakeWidgetWithTag for Dynamic<WidgetInstance> {
fn make_with_tag(self, id: crate::widget::WidgetTag) -> WidgetInstance {
self.into_switcher().make_with_tag(id)
}
}
impl MakeWidgetWithId for Dynamic<Option<WidgetInstance>> {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
impl MakeWidgetWithTag for Dynamic<Option<WidgetInstance>> {
fn make_with_tag(self, id: crate::widget::WidgetTag) -> WidgetInstance {
self.map_each(|widget| {
widget
.as_ref()
.map_or_else(|| Space::clear().make_widget(), Clone::clone)
})
.make_with_id(id)
.make_with_tag(id)
}
}
@ -1260,7 +1260,7 @@ impl<T> DynamicReader<T> {
/// This function panics if this value is already locked by the current
/// thread.
#[must_use]
pub fn get_tracking_refresh(&mut self, context: &WidgetContext<'_, '_>) -> T
pub fn get_tracking_redraw(&mut self, context: &WidgetContext<'_, '_>) -> T
where
T: Clone,
{
@ -1649,7 +1649,7 @@ impl<T> Value<T> {
///
/// If `self` is a dynamic, `context` will be refreshed when the value is
/// updated.
pub fn get_tracked(&self, context: &WidgetContext<'_, '_>) -> T
pub fn get_tracking_redraw(&self, context: &WidgetContext<'_, '_>) -> T
where
T: Clone,
{

View file

@ -52,25 +52,214 @@ use crate::{ConstraintLimit, Run};
/// This type can go by many names in other UI frameworks: View, Component,
/// Control.
///
/// # Widgets are hierarchical
///
/// Gooey's widgets are organized in a hierarchical structure: widgets can
/// contain other widgets. A window in Gooey contains a single root widget,
/// which may contain one or more additional widgets.
///
/// # How Widgets are created
///
/// Gooey offers several approaches to creating widgets. The primary trait that
/// is used to instantiate a widget is [`MakeWidget`]. This trait is
/// automatically implemented for all types that implement [`Widget`].
///
/// [`MakeWidget::make_widget`] is responsible for returning a
/// [`WidgetInstance`]. This is a wrapper for a type that implements [`Widget`]
/// that can be used without knowing the original type of the [`Widget`].
///
/// While all [`MakeWidget`] is automatically implemented for all [`Widget`]
/// types, it can also be implemented by types that do not implement [`Widget`].
/// This is a useful strategy when designing reusable widgets that are able to
/// be completely represented by composing existing widgets. The
/// [`ProgressBar`](crate::widgets::ProgressBar) type uses this strategy, as it
/// uses either a [`Spinner`](crate::widgets::progress::Spinner) or a
/// [`Slider`](crate::widgets::Slider) to show its progress.
///
/// One last convenience trait is provided to help create widgets that contain
/// exactly one child: [`WrapperWidget`]. [`WrapperWidget`] exposes most of the
/// same functions, but provides purpose-built functions for tweaking child's
/// layout and rendering behavior to minimize the amount of redundant code
/// between these types of widgets.
///
/// # Identifying Widgets
///
/// Once a widget has been instantiated as a [`WidgetInstance`], it will be
/// assigned a unique [`WidgetId`]. Sometimes, it may be helpful to pre-create a
/// [`WidgetId`] before the widget has been created. For these situations,
/// [`WidgetTag`] allows creating a tag that can be passed to
/// [`MakeWidgetWithTag::make_with_tag`] to set the returned
/// [`WidgetInstance`]'s id.
///
/// # How to "talk" to another widget
///
/// Once a widget has been wrapped inside of a [`WidgetInstance`], it is no
/// longer possible to invoke [`Widget`]/s functions directly. Instead, a
/// context must be created for that widget. In each of the [`Widget`]
/// functions, a context is provided that represents the current widget. Each
/// context type has a `for_other()` function that accepts any widget type: a
/// [`WidgetId`], a [`WidgetInstance`], a [`MountedWidget`], or a [`WidgetRef`].
/// The returned context will represent the associate widget, allowing access to
/// the exposed APIs through the context.
///
/// While [`WidgetInstance::lock`] can be used to gain access to the underlying
/// [`Widget`] type, this behavior should only be reserved for limited
/// situations. It should be preferred to pass data between widgets using
/// [`Dynamic`]s or style components if possible. This ensures that your code
/// can work with as many other widgets as possible, instead of restricting
/// features to a specific set of types.
///
/// # How layout and rendering works
///
/// TODO write layout + rendering docs
/// When a window is rendered, the root widget has its
/// [`layout()`](Self::layout) function called with both constraints specifying
/// [`ConstraintLimit::SizeToFit`] with the window's inner size. The root widget
/// measures its content to try to fit within the specified constraints, and
/// returns its calculated size. If a widget has children, it can invoke
/// [`LayoutContext::layout()`] on a context for each of its children to
/// determine their required sizes.
///
/// # Hit Testing
/// Next, the window sets the root's layout. When a widget contains another
/// widget, it must call [`LayoutContext::set_child_layout`] for the child to be
/// able to be rendered. This tells Gooey the location to draw the widget. While
/// it is possible to provide any rectangle, Gooey clips all widgets and their
/// children so that they cannot draw outside of their assigned bounds.
///
/// TODO write hit testing docs
/// Once the layout has been determined, the window will invoke the root
/// widget's [`redraw()`](Self::redraw) function. If a widget contains one or
/// more children, it needs to invoke [`GraphicsContext::redraw()`] on a context
/// for each of its children during its own render function. This allows full
/// control over the order of drawing calls, allowing widgets to draw behind,
/// in-between, or in front of their children.
///
/// The last responsibility the window has each frame is size adjustment. The
/// window will potentially adjust its size automatically based on the root
/// widget's [`root_behavior()`](Self::root_behavior).
///
/// # Controlling Invalidation and Redrawing
///
/// Gooey only redraws window contents when requested by the operating system or
/// a tracked [`Dynamic`] is updated. Similarly, Gooey caches the known layout
/// sizes and locations for widgets unless they are *invalidated*. Invalidation
/// is done automatically when the window size changes or a tracked [`Dynamic`]
/// is updated.
///
/// These systems require Gooey to track which [`Dynamic`] values a widget
/// depends on for redrawing and invalidation. During a widget's redraw and
/// layout functions, it needs to ensure that all depended upon [`Dynamic`]s are
/// tracked using one of the various
/// `*_tracking_redraw()`/`*_tracking_invalidate()` functions. For example,
/// [`Dynamic::get_tracking_redraw()`] and
/// [`Dynamic::get_tracking_invalidate()`].
///
/// # Hover State: Hit Testing
///
/// Before any cursor-related events are sent to a widget, the cursor's position
/// is tested with [`Widget::hit_test`]. When a widget returns true for a
/// position, it is eligible to receive events such as mouse buttons.
///
/// When a widget returns false, it will not receive any cursor related events
/// with one exception: hover events. Hover events will fire for widgets whose
/// children are currently being hovered, regardless of whether
/// [`Widget::hit_test`] returned true.
///
/// The provided [`Widget::hit_test`] implementation returns false.
///
/// As the cursor moves across the window, the window will look at the render
/// information to see what widgets are positioned under the cursor and the
/// order in which they were drawn. Beginning at the topmost widget,
/// [`Widget::hit_test`] is called on each widget.
///
/// The currently hovered widget state is tracked for events that target widgets
/// beneath the current cursor.
///
/// # Mouse Button Events
///
/// TODO write mouse button docs
/// When a window receives an event for a mouse button being pressed, it calls
/// the hovered widget's [`mouse_down()`](Self::mouse_down) function. If the
/// function returns [`HANDLED`]/[`ControlFlow::Break`], the widget becomes the
/// *tracking* widget for that mouse button.
///
/// If the widget returns [`IGNORED`]/[`ControlFlow::Continue`], the window will
/// call the parent's `mouse_down()` function. This repeats until the root
/// widget is reached or a widget returns `HANDLED`.
///
/// Once a tracking widget is found, any cursor-related movements will cause
/// [`Widget::mouse_drag()`] to be called. Upon the mouse button being released,
/// the tracking widget's [`mouse_up()`](Self::mouse_up) function will be
/// called.
///
/// # User Input Focus
///
/// TODO write focus docs
/// A window can have a widget be *focused* for user input. For example, a text
/// [`Input`](crate::widgets::Input) only responds to keyboard input once user
/// input focus has been directed at the widget. This state is generally
/// represented by drawing the theme's highlight color around the border of the
/// widget. [`GraphicsContext::draw_focus_ring`] can be used to draw the
/// standard focus ring for rectangular-shaped widgets.
///
/// The most direct way to give a widget focus is to call
/// [`WidgetContext::focus`]. However, not all widgets can accept focus. If a
/// widget returns true from its [`accept_focus()`](Self::accept_focus)
/// function, focus will be given to it and its [`focus()`](Self::focus)
/// function will be invoked.
///
/// If a widget returns false from its `accept_focus()` function, the window
/// will perform these steps:
///
/// 1. If the widget has any children, sort its children visually and attempt to
/// focus each one until a widget accepts focus. If any of these children
/// have children, those children should also be checked.
/// 2. The widget asks its parent to find the next focus after itself. The
/// parent finds the current widget in that list and attempts to focus each
/// widget after the current widget in the visual order.
/// 3. This repeats until the root widget is reached, at which point focus is
/// attempted using this algorithm until either a focused widget is found or
/// the original widget is reached again. If no widget can be found in a full
/// cycle of the widget tree, focus will be cleared.
///
/// When a window first opens, it call [`focus()`][WidgetContext::focus] on the
/// root widget's context.
///
/// ## Losing Focus
///
/// A Widget can deny the ability for focus to be taken away from it by
/// returning `false` from [`Widget::allow_blur()`]. In general, widgets should
/// not do this. However, some user interfaces are designed to always keep focus
/// on a single widget, and this feature enables that functionality.
///
/// When a widget currently has focused and loses it, its [`blur()`](Self::blur)
/// function will be invoked.
///
/// # Styling
///
/// TODO write styling docs
/// Gooey allows widgets to receive styling information through the widget
/// hierarchy using [`Styles`]. Gooey calculates the effectives styles for each
/// widget by inheriting all inheritable styles from its parent.
///
/// The [`Style`] widget allows assigining [`Styles`] to all of its children
/// widget. It works by calling [`WidgetContext::attach_styles`], and Gooey
/// takes care of the rest.
///
/// Styling in Gooey aims to be simple, easy-to-understand, and extensible.
///
/// # Color Themes
///
/// Gooey aims to make it easy for developers to customize the appearance of its
/// applications. The way color themes work in Gooey begins with the
/// [`ColorScheme`](crate::styles::ColorScheme). A color scheme is a set of
/// [`ColorSource`](crate::styles::ColorSource) that are used to generate a
/// variety of shades of colors for various roles color plays in a user
/// interface. In a way, coloring Gooey apps is a bit like paint-by-number,
/// where the number is the name of the color role.
///
/// A `ColorScheme` can be used to create a [`ThemePair`], which is theme
/// definition that a theme for light and dark mode.
///
/// In [the repository][repo], the `theme` example is a good way to explore how
/// the color system works in Gooey.
///
/// [repo]: https://github.com/khonsulabs/gooey
pub trait Widget: Send + UnwindSafe + Debug + 'static {
/// Redraw the contents of this widget.
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>);
@ -1107,26 +1296,26 @@ pub trait MakeWidget: Sized {
/// A type that can create a [`WidgetInstance`] with a preallocated
/// [`WidgetId`].
pub trait MakeWidgetWithId: Sized {
/// Returns a new [`WidgetInstance`] whose [`WidgetId`] is `id`.
fn make_with_id(self, id: WidgetTag) -> WidgetInstance;
pub trait MakeWidgetWithTag: Sized {
/// Returns a new [`WidgetInstance`] whose [`WidgetId`] comes from `tag`.
fn make_with_tag(self, tag: WidgetTag) -> WidgetInstance;
}
impl<T> MakeWidgetWithId for T
impl<T> MakeWidgetWithTag for T
where
T: Widget,
{
fn make_with_id(self, id: WidgetTag) -> WidgetInstance {
fn make_with_tag(self, id: WidgetTag) -> WidgetInstance {
WidgetInstance::with_id(self, id)
}
}
impl<T> MakeWidget for T
where
T: MakeWidgetWithId,
T: MakeWidgetWithTag,
{
fn make_widget(self) -> WidgetInstance {
self.make_with_id(WidgetTag::unique())
self.make_with_tag(WidgetTag::unique())
}
}
@ -1136,9 +1325,9 @@ impl MakeWidget for WidgetInstance {
}
}
impl MakeWidgetWithId for Color {
fn make_with_id(self, id: WidgetTag) -> WidgetInstance {
Space::colored(self).make_with_id(id)
impl MakeWidgetWithTag for Color {
fn make_with_tag(self, id: WidgetTag) -> WidgetInstance {
Space::colored(self).make_with_tag(id)
}
}
@ -1490,19 +1679,19 @@ where
/// A [`Widget`] that has been attached to a widget hierarchy.
#[derive(Clone)]
pub struct ManagedWidget {
pub struct MountedWidget {
pub(crate) node_id: LotId,
pub(crate) widget: WidgetInstance,
pub(crate) tree: Tree,
}
impl Debug for ManagedWidget {
impl Debug for MountedWidget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self.widget, f)
}
}
impl ManagedWidget {
impl MountedWidget {
/// Locks the widget for exclusive access. Locking widgets should only be
/// done for brief moments of time when you are certain no deadlocks can
/// occur due to other widget locks being held.
@ -1537,7 +1726,7 @@ impl ManagedWidget {
/// This function returns the value set in
/// [`MakeWidget::with_next_focus()`].
#[must_use]
pub fn next_focus(&self) -> Option<ManagedWidget> {
pub fn next_focus(&self) -> Option<MountedWidget> {
self.widget
.next_focus()
.and_then(|next_focus| self.tree.widget(next_focus))
@ -1548,14 +1737,14 @@ impl ManagedWidget {
/// There is no direct way to set this value. This relationship is created
/// automatically using [`MakeWidget::with_next_focus()`].
#[must_use]
pub fn previous_focus(&self) -> Option<ManagedWidget> {
pub fn previous_focus(&self) -> Option<MountedWidget> {
self.tree.previous_focus(self.id())
}
/// Returns the next or previous focus target, if one was set using
/// [`MakeWidget::with_next_focus()`].
#[must_use]
pub fn explicit_focus_target(&self, advance: bool) -> Option<ManagedWidget> {
pub fn explicit_focus_target(&self, advance: bool) -> Option<MountedWidget> {
if advance {
self.next_focus()
} else {
@ -1605,7 +1794,7 @@ impl ManagedWidget {
/// Returns the parent of this widget.
#[must_use]
pub fn parent(&self) -> Option<ManagedWidget> {
pub fn parent(&self) -> Option<MountedWidget> {
self.tree
.parent(self.node_id)
.and_then(|id| self.tree.widget_from_node(id))
@ -1643,24 +1832,24 @@ impl ManagedWidget {
self.tree.persist_layout(self.node_id, constraints, size);
}
pub(crate) fn visually_ordered_children(&self, order: VisualOrder) -> Vec<ManagedWidget> {
pub(crate) fn visually_ordered_children(&self, order: VisualOrder) -> Vec<MountedWidget> {
self.tree.visually_ordered_children(self.node_id, order)
}
}
impl AsRef<WidgetId> for ManagedWidget {
impl AsRef<WidgetId> for MountedWidget {
fn as_ref(&self) -> &WidgetId {
self.widget.as_ref()
}
}
impl PartialEq for ManagedWidget {
impl PartialEq for MountedWidget {
fn eq(&self, other: &Self) -> bool {
self.widget == other.widget
}
}
impl PartialEq<WidgetInstance> for ManagedWidget {
impl PartialEq<WidgetInstance> for MountedWidget {
fn eq(&self, other: &WidgetInstance) -> bool {
&self.widget == other
}
@ -1915,7 +2104,7 @@ pub enum ChildrenSyncChange {
/// contain multiple children widgets. It is used in conjunction with a
/// `Value<Children>`.
#[derive(Debug)]
pub struct MountedChildren<T = ManagedWidget> {
pub struct MountedChildren<T = MountedWidget> {
generation: Option<Generation>,
children: Vec<T>,
}
@ -1990,23 +2179,23 @@ impl<T> Default for MountedChildren<T> {
/// A child in a [`MountedChildren`] collection.
pub trait MountableChild: Sized {
/// Returns the mounted representation of `widget`.
fn mount(widget: ManagedWidget, into: &MountedChildren<Self>, index: usize) -> Self;
fn mount(widget: MountedWidget, into: &MountedChildren<Self>, index: usize) -> Self;
/// Returns the widget and performs any other cleanup for this widget being unmounted.q
fn unmount(self) -> ManagedWidget;
fn unmount(self) -> MountedWidget;
/// Returns a reference to the widget.
fn widget(&self) -> &ManagedWidget;
fn widget(&self) -> &MountedWidget;
}
impl MountableChild for ManagedWidget {
fn mount(widget: ManagedWidget, _into: &MountedChildren<Self>, _index: usize) -> Self {
impl MountableChild for MountedWidget {
fn mount(widget: MountedWidget, _into: &MountedChildren<Self>, _index: usize) -> Self {
widget
}
fn widget(&self) -> &ManagedWidget {
fn widget(&self) -> &MountedWidget {
self
}
fn unmount(self) -> ManagedWidget {
fn unmount(self) -> MountedWidget {
self
}
}
@ -2017,7 +2206,7 @@ pub enum WidgetRef {
/// An unmounted child widget
Unmounted(WidgetInstance),
/// A mounted child widget
Mounted(ManagedWidget),
Mounted(MountedWidget),
}
impl WidgetRef {
@ -2037,7 +2226,7 @@ impl WidgetRef {
pub fn mounted<'window>(
&mut self,
context: &mut impl AsEventContext<'window>,
) -> ManagedWidget {
) -> MountedWidget {
self.mount_if_needed(context);
let Self::Mounted(widget) = self else {
@ -2101,14 +2290,14 @@ impl WidgetId {
/// Finds this widget mounted in this window, if present.
#[must_use]
pub fn find_in(self, context: &WidgetContext<'_, '_>) -> Option<ManagedWidget> {
pub fn find_in(self, context: &WidgetContext<'_, '_>) -> Option<MountedWidget> {
context.widget().tree.widget(self)
}
}
/// A [`WidgetId`] that has not been assigned to a [`WidgetInstance`].
///
/// This type is passed to [`MakeWidgetWithId::make_with_id()`] to create a
/// This type is passed to [`MakeWidgetWithTag::make_with_tag()`] to create a
/// [`WidgetInstance`] with a preallocated id.
///
/// This type cannot be cloned or copied to ensure only a single widget can be

View file

@ -226,7 +226,7 @@ impl Button {
}
fn determine_stateful_colors(&mut self, context: &mut WidgetContext<'_, '_>) -> ButtonColors {
let kind = self.kind.get_tracked(context);
let kind = self.kind.get_tracking_redraw(context);
let visual_state = Self::visual_style(context);
self.cached_state = CacheState {
@ -352,7 +352,7 @@ impl Widget for Button {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
#![allow(clippy::similar_names)]
let current_style = self.kind.get_tracked(context);
let current_style = self.kind.get_tracking_redraw(context);
if self.cached_state.key != context.cache_key() || self.cached_state.kind != current_style {
self.update_colors(context, false);
}

View file

@ -11,7 +11,7 @@ use crate::context::{GraphicsContext, LayoutContext};
use crate::styles::components::{LineHeight, OutlineColor, TextColor, WidgetAccentColor};
use crate::styles::Dimension;
use crate::value::{Dynamic, DynamicReader, IntoDynamic, IntoValue, Value};
use crate::widget::{MakeWidget, MakeWidgetWithId, Widget, WidgetInstance};
use crate::widget::{MakeWidget, MakeWidgetWithTag, Widget, WidgetInstance};
use crate::widgets::button::ButtonKind;
use crate::ConstraintLimit;
@ -51,8 +51,8 @@ impl Checkbox {
}
}
impl MakeWidgetWithId for Checkbox {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
impl MakeWidgetWithTag for Checkbox {
fn make_with_tag(self, id: crate::widget::WidgetTag) -> WidgetInstance {
CheckboxOrnament {
value: self.state.create_reader(),
}
@ -64,7 +64,7 @@ impl MakeWidgetWithId for Checkbox {
*value = !*value;
})
.kind(self.kind)
.make_with_id(id)
.make_with_tag(id)
}
}
@ -192,7 +192,7 @@ impl Widget for CheckboxOrnament {
);
let stroke_options = StrokeOptions::lp_wide(Lp::points(2)).into_px(context.gfx.scale());
match self.value.get_tracking_refresh(context) {
match self.value.get_tracking_redraw(context) {
state @ (CheckboxState::Checked | CheckboxState::Indeterminant) => {
let color = context.get(&WidgetAccentColor);
context

View file

@ -73,7 +73,7 @@ impl Widget for ColorSourcePicker {
let loupe_size = Lp::mm(3).into_px(context.gfx.scale());
let size = context.gfx.region().size;
let value = self.value.get_tracking_refresh(context);
let value = self.value.get_tracking_redraw(context);
let value_pos = self.visible_rect.origin
+ Point::new(
if self.hue_is_360 {
@ -84,7 +84,7 @@ impl Widget for ColorSourcePicker {
self.visible_rect.size.height * *value.saturation.one_minus(),
);
let lightness = self.lightness.get_tracked(context);
let lightness = self.lightness.get_tracking_redraw(context);
let value_color = value.color(lightness);
let outline_color = if context.focused(true) {

View file

@ -544,7 +544,9 @@ impl WrapperWidget for Custom {
}
fn background_color(&mut self, context: &WidgetContext<'_, '_>) -> Option<Color> {
self.background.as_ref().map(|bg| bg.get_tracked(context))
self.background
.as_ref()
.map(|bg| bg.get_tracking_redraw(context))
}
fn mounted(&mut self, context: &mut EventContext<'_, '_>) {

View file

@ -16,7 +16,7 @@ use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContex
use crate::styles::components::IntrinsicPadding;
use crate::styles::Dimension;
use crate::value::{Generation, IntoValue, Value};
use crate::widget::{MakeWidget, ManagedWidget, Widget, WidgetInstance};
use crate::widget::{MakeWidget, MountedWidget, Widget, WidgetInstance};
use crate::ConstraintLimit;
/// A 2D grid of widgets.
@ -24,7 +24,7 @@ use crate::ConstraintLimit;
pub struct Grid<const ELEMENTS: usize> {
columns: Value<[GridDimension; ELEMENTS]>,
rows: Value<GridWidgets<ELEMENTS>>,
live_rows: Vec<[ManagedWidget; ELEMENTS]>,
live_rows: Vec<[MountedWidget; ELEMENTS]>,
layout: GridLayout,
layout_generation: Option<Generation>,
spec_generation: Option<Generation>,

View file

@ -1019,7 +1019,7 @@ where
}
self.blink_state.update(context.elapsed());
let window_focused = context.window().focused().get_tracking_refresh(context);
let window_focused = context.window().focused().get_tracking_redraw(context);
if window_focused != self.window_focused {
if window_focused {
self.blink_state.force_on();

View file

@ -94,9 +94,9 @@ impl Widget for Label {
macro_rules! impl_make_widget {
($($type:ty),*) => {
$(impl crate::widget::MakeWidgetWithId for $type {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
Label::new(self).make_with_id(id)
$(impl crate::widget::MakeWidgetWithTag for $type {
fn make_with_tag(self, id: crate::widget::WidgetTag) -> WidgetInstance {
Label::new(self).make_with_tag(id)
}
})*
};

View file

@ -16,7 +16,7 @@ use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContex
use crate::utils::IgnorePoison;
use crate::value::{Dynamic, DynamicGuard, IntoValue, Value};
use crate::widget::{
Callback, Children, MakeWidget, ManagedWidget, MountedChildren, Widget, WidgetId, WidgetRef,
Callback, Children, MakeWidget, MountedChildren, MountedWidget, Widget, WidgetId, WidgetRef,
WrapperWidget,
};
use crate::widgets::container::ContainerShadow;
@ -182,7 +182,7 @@ impl Widget for OverlayLayer {
continue;
};
let opacity = child.opacity.get_tracking_refresh(context);
let opacity = child.opacity.get_tracking_redraw(context);
let mut context = context.for_other(mounted);
context.apply_opacity(opacity);
context.redraw();
@ -376,7 +376,7 @@ impl OverlayState {
fn layout_overlay_relative(
&mut self,
index: usize,
widget: &ManagedWidget,
widget: &MountedWidget,
available_space: Size<UPx>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
relative_to: WidgetId,
@ -495,7 +495,7 @@ impl OverlayState {
fn layout_overlay(
&mut self,
index: usize,
widget: &ManagedWidget,
widget: &MountedWidget,
available_space: Size<UPx>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
) -> Option<Rect<Px>> {

View file

@ -13,7 +13,7 @@ use crate::animation::{
AnimationHandle, AnimationTarget, IntoAnimate, PercentBetween, Spawn, ZeroToOne,
};
use crate::value::{Dynamic, IntoDynamic, IntoValue, MapEach, Value};
use crate::widget::{MakeWidget, MakeWidgetWithId, Widget, WidgetInstance};
use crate::widget::{MakeWidget, MakeWidgetWithTag, Widget, WidgetInstance};
use crate::widgets::slider::{InactiveTrackColor, Slidable, TrackColor, TrackSize};
use crate::widgets::Data;
@ -60,8 +60,8 @@ pub enum Progress<T = ZeroToOne> {
Percent(T),
}
impl MakeWidgetWithId for ProgressBar {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
impl MakeWidgetWithTag for ProgressBar {
fn make_with_tag(self, id: crate::widget::WidgetTag) -> WidgetInstance {
let start = Dynamic::new(ZeroToOne::ZERO);
let end = Dynamic::new(ZeroToOne::ZERO);
let value = (&start, &end).map_each(|(start, end)| *start..=*end);
@ -76,12 +76,16 @@ impl MakeWidgetWithId for ProgressBar {
end: end.clone(),
degree_offset: degree_offset.clone(),
}
.make_with_id(id),
.make_with_tag(id),
Some(degree_offset),
)
} else {
(
value.slider().knobless().non_interactive().make_with_id(id),
value
.slider()
.knobless()
.non_interactive()
.make_with_tag(id),
None,
)
};
@ -332,8 +336,8 @@ impl Spinner {
impl Widget for Spinner {
fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>) {
let track_size = context.get(&TrackSize).into_px(context.gfx.scale());
let start = self.start.get_tracking_refresh(context);
let end = self.end.get_tracking_refresh(context);
let start = self.start.get_tracking_redraw(context);
let end = self.end.get_tracking_redraw(context);
let size = context.gfx.region().size;
let render_size = size.width.min(size.height);
let radius = render_size / 2 - track_size;

View file

@ -11,7 +11,7 @@ use crate::context::{GraphicsContext, LayoutContext};
use crate::styles::components::{LineHeight, OutlineColor, WidgetAccentColor};
use crate::styles::Dimension;
use crate::value::{Dynamic, DynamicReader, IntoDynamic, IntoValue, Value};
use crate::widget::{MakeWidget, MakeWidgetWithId, Widget, WidgetInstance};
use crate::widget::{MakeWidget, MakeWidgetWithTag, Widget, WidgetInstance};
use crate::widgets::button::ButtonKind;
use crate::ConstraintLimit;
@ -51,11 +51,11 @@ impl<T> Radio<T> {
}
}
impl<T> MakeWidgetWithId for Radio<T>
impl<T> MakeWidgetWithTag for Radio<T>
where
T: Clone + Debug + Eq + UnwindSafe + Send + 'static,
{
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
fn make_with_tag(self, id: crate::widget::WidgetTag) -> WidgetInstance {
RadioOrnament {
value: self.value.clone(),
state: self.state.create_reader(),
@ -67,7 +67,7 @@ where
self.state.set(self.value.clone());
})
.kind(self.kind)
.make_with_id(id)
.make_with_tag(id)
}
}

View file

@ -7,7 +7,7 @@ use kludgine::Color;
use crate::styles::components::OutlineColor;
use crate::styles::{Component, DynamicComponent};
use crate::value::{Dynamic, IntoDynamic, IntoValue, MapEach, Value};
use crate::widget::{MakeWidget, MakeWidgetWithId, WidgetInstance};
use crate::widget::{MakeWidget, MakeWidgetWithTag, WidgetInstance};
use crate::widgets::button::{ButtonBackground, ButtonHoverBackground, ButtonKind};
/// A selectable, labeled widget representing a value.
@ -46,11 +46,11 @@ impl<T> Select<T> {
}
}
impl<T> MakeWidgetWithId for Select<T>
impl<T> MakeWidgetWithTag for Select<T>
where
T: Clone + Debug + Eq + RefUnwindSafe + UnwindSafe + Send + Sync + 'static,
{
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
fn make_with_tag(self, id: crate::widget::WidgetTag) -> WidgetInstance {
let selected = self.state.map_each({
let value = self.value.clone();
move |state| state == &value
@ -58,7 +58,7 @@ where
let selected_color = DynamicComponent::new({
let selected = selected.clone();
move |context| {
if selected.get_tracking_refresh(context) {
if selected.get_tracking_redraw(context) {
Some(Component::Color(context.get(&SelectedColor)))
} else {
None
@ -80,7 +80,7 @@ where
.kind(kind)
.with_dynamic(&ButtonBackground, selected_color.clone())
.with_dynamic(&ButtonHoverBackground, selected_color)
.make_with_id(id)
.make_with_tag(id)
}
}

View file

@ -443,9 +443,9 @@ where
let half_knob = knob_size / 2;
let (mut start_value, mut end_value) =
T::into_parts(self.value.get_tracking_refresh(context));
let min = self.minimum.get_tracked(context);
let mut max = self.maximum.get_tracked(context);
T::into_parts(self.value.get_tracking_redraw(context));
let min = self.minimum.get_tracking_redraw(context);
let mut max = self.maximum.get_tracking_redraw(context);
if max < min {
self.maximum.map_mut(|max| *max = min.clone());

View file

@ -39,7 +39,7 @@ impl Space {
impl Widget for Space {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
let color = self.color.get_tracked(context);
let color = self.color.get_tracking_redraw(context);
context.fill(color);
}

View file

@ -7,7 +7,7 @@ use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContex
use crate::styles::components::IntrinsicPadding;
use crate::styles::FlexibleDimension;
use crate::value::{Generation, IntoValue, Value};
use crate::widget::{Children, ChildrenSyncChange, ManagedWidget, Widget, WidgetRef};
use crate::widget::{Children, ChildrenSyncChange, MountedWidget, Widget, WidgetRef};
use crate::widgets::grid::{GridDimension, GridLayout, Orientation};
use crate::widgets::{Expand, Resize};
use crate::ConstraintLimit;
@ -24,7 +24,7 @@ pub struct Stack {
layout: GridLayout,
layout_generation: Option<Generation>,
// TODO Refactor synced_children into its own type.
synced_children: Vec<ManagedWidget>,
synced_children: Vec<MountedWidget>,
}
impl Stack {
@ -68,7 +68,7 @@ impl Stack {
self.children.map(|children| {
children.synchronize_with(
&mut self.synced_children,
|this, index| this.get(index).map(ManagedWidget::instance),
|this, index| this.get(index).map(MountedWidget::instance),
|this, change| match change {
ChildrenSyncChange::Insert(index, widget) => {
// This is a brand new child.

View file

@ -9,7 +9,7 @@ use crate::styles::components::{
};
use crate::styles::Dimension;
use crate::value::{Dynamic, IntoDynamic, IntoValue, MapEach, Validation, Value};
use crate::widget::{MakeWidget, MakeWidgetWithId, WidgetInstance, WidgetRef, WrapperWidget};
use crate::widget::{MakeWidget, MakeWidgetWithTag, WidgetInstance, WidgetRef, WrapperWidget};
/// A widget that displays validation information around another widget.
///
@ -46,8 +46,8 @@ impl Validated {
}
}
impl MakeWidgetWithId for Validated {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
impl MakeWidgetWithTag for Validated {
fn make_with_tag(self, id: crate::widget::WidgetTag) -> WidgetInstance {
let message = match self.hint {
Value::Constant(hint) => self
.validation
@ -84,7 +84,7 @@ impl MakeWidgetWithId for Validated {
error_color,
default_color,
}
.make_with_id(id)
.make_with_tag(id)
}
}

View file

@ -39,7 +39,7 @@ use crate::tree::Tree;
use crate::utils::ModifiersExt;
use crate::value::{Dynamic, DynamicReader, IntoDynamic, IntoValue, Value};
use crate::widget::{
EventHandling, ManagedWidget, RootBehavior, Widget, WidgetId, WidgetInstance, HANDLED, IGNORED,
EventHandling, MountedWidget, RootBehavior, Widget, WidgetId, WidgetInstance, HANDLED, IGNORED,
};
use crate::window::sealed::WindowCommand;
use crate::{initialize_tracing, ConstraintLimit, Run};
@ -338,7 +338,7 @@ pub trait WindowBehavior: Sized + 'static {
struct GooeyWindow<T> {
behavior: T,
root: ManagedWidget,
root: MountedWidget,
contents: Drawing,
should_close: bool,
cursor: CursorState,
@ -1221,7 +1221,7 @@ where
fn recursively_handle_event(
context: &mut EventContext<'_, '_>,
mut each_widget: impl FnMut(&mut EventContext<'_, '_>) -> EventHandling,
) -> Option<ManagedWidget> {
) -> Option<MountedWidget> {
match each_widget(context) {
HANDLED => Some(context.widget().clone()),
IGNORED => context.parent().and_then(|parent| {