Skip to content

Commit

Permalink
Merge pull request #327 from kas-gui/work
Browse files Browse the repository at this point in the history
Rename Draw/SizeHandle → ThemeDraw/Size; revise SizeMgr; #[extends] macro for ThemeDraw
  • Loading branch information
dhardy authored Jun 10, 2022
2 parents ae139dc + 9a660a9 commit bb46d25
Show file tree
Hide file tree
Showing 32 changed files with 639 additions and 531 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ KAS optionally uses several Rust nightly features, but is functional without
Usage of `unsafe` is allowed, but not preferred. Current use cases:

- Defining constants requiring `unwrap` (tracker: [`const_option`](https://github.com/rust-lang/rust/issues/58732)). Note that since 1.57, `panic!` in const fns is supported, hence a work-around using `match` is possible.
- To get around lifetime restrictions on the `draw_handle` and `size_handle`
- To get around lifetime restrictions on the theme API's `Theme::draw` and `Window::size`
methods; this will no longer require `unsafe` once the
`generic_associated_types` feature is stabilised.
- `WidgetId` uses `unsafe` code to support both inline and heap-allocated
Expand Down
5 changes: 5 additions & 0 deletions crates/kas-core/src/core/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@ pub trait Layout {
/// It is expected that [`Self::set_rect`] is called before this method,
/// but failure to do so should not cause a fatal error.
///
/// The `draw` parameter is pre-parameterized with this widget's
/// [`WidgetId`], allowing drawn components to react to input state. This
/// implies that when calling `draw` on children, the child's `id` must be
/// supplied via [`DrawMgr::re_id`] or [`DrawMgr::recurse`].
///
/// Default implementation:
///
/// - No default implementation, except,
Expand Down
9 changes: 8 additions & 1 deletion crates/kas-core/src/draw/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use super::{color::Rgba, AnimationState};
#[allow(unused)]
use super::{DrawRounded, DrawRoundedImpl};
use super::{DrawSharedImpl, ImageId, PassId, PassType, SharedState};
use super::{DrawShared, DrawSharedImpl, ImageId, PassId, PassType, SharedState};
use crate::geom::{Offset, Quad, Rect, Vec2};
#[allow(unused)]
use crate::text::TextApi;
Expand Down Expand Up @@ -123,6 +123,9 @@ impl<'a, DS: DrawSharedImpl> DrawIface<'a, DS> {
/// accessing these requires reconstruction of the implementing type via
/// [`DrawIface::downcast_from`].
pub trait Draw {
/// Access shared draw state
fn shared(&mut self) -> &mut dyn DrawShared;

/// Request redraw at the next frame time
///
/// Animations should call this each frame until complete.
Expand Down Expand Up @@ -224,6 +227,10 @@ pub trait Draw {
}

impl<'a, DS: DrawSharedImpl> Draw for DrawIface<'a, DS> {
fn shared(&mut self) -> &mut dyn DrawShared {
self.shared
}

fn animate(&mut self) {
self.draw.animate();
}
Expand Down
4 changes: 2 additions & 2 deletions crates/kas-core/src/event/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use linear_map::LinearMap;
use log::{trace, warn};
use smallvec::SmallVec;
use std::any::Any;
use std::any::{type_name, Any};
use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap, VecDeque};
use std::ops::{Deref, DerefMut};
Expand Down Expand Up @@ -369,7 +369,7 @@ struct Message {
impl Message {
fn new<M: Any + Debug>(msg: Box<M>) -> Self {
#[cfg(debug_assertions)]
let fmt = format!("{:?}", &msg);
let fmt = format!("{}::{:?}", type_name::<M>(), &msg);
let any = msg;
Message {
#[cfg(debug_assertions)]
Expand Down
19 changes: 9 additions & 10 deletions crates/kas-core/src/event/manager/mgr_pub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,21 +652,20 @@ impl<'a> EventMgr<'a> {
/// See also notes on [`Widget::configure`].
pub fn size_mgr<F: FnMut(SizeMgr) -> T, T>(&mut self, mut f: F) -> T {
let mut result = None;
self.shell.size_and_draw_shared(&mut |size_handle, _| {
result = Some(f(SizeMgr::new(size_handle)));
self.shell.size_and_draw_shared(&mut |size, _| {
result = Some(f(SizeMgr::new(size)));
});
result.expect("ShellWindow::size_handle impl failed to call function argument")
result.expect("ShellWindow::size_and_draw_shared impl failed to call function argument")
}

/// Access a [`SetRectMgr`]
pub fn set_rect_mgr<F: FnMut(&mut SetRectMgr) -> T, T>(&mut self, mut f: F) -> T {
let mut result = None;
self.shell
.size_and_draw_shared(&mut |size_handle, draw_shared| {
let mut mgr = SetRectMgr::new(size_handle, draw_shared, self.state);
result = Some(f(&mut mgr));
});
result.expect("ShellWindow::size_handle impl failed to call function argument")
self.shell.size_and_draw_shared(&mut |size, draw_shared| {
let mut mgr = SetRectMgr::new(size, draw_shared, self.state);
result = Some(f(&mut mgr));
});
result.expect("ShellWindow::size_and_draw_shared impl failed to call function argument")
}

/// Access a [`DrawShared`]
Expand All @@ -675,7 +674,7 @@ impl<'a> EventMgr<'a> {
self.shell.size_and_draw_shared(&mut |_, draw_shared| {
result = Some(f(draw_shared));
});
result.expect("ShellWindow::draw_shared impl failed to call function argument")
result.expect("ShellWindow::size_and_draw_shared impl failed to call function argument")
}

/// Grab "press" events for `source` (a mouse or finger)
Expand Down
4 changes: 2 additions & 2 deletions crates/kas-core/src/event/manager/mgr_shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ impl EventState {

self.new_accel_layer(WidgetId::ROOT, false);

shell.size_and_draw_shared(&mut |size_handle, draw_shared| {
let mut mgr = SetRectMgr::new(size_handle, draw_shared, self);
shell.size_and_draw_shared(&mut |size, draw_shared| {
let mut mgr = SetRectMgr::new(size, draw_shared, self);
mgr.configure(WidgetId::ROOT, widget);
});

Expand Down
19 changes: 14 additions & 5 deletions crates/kas-core/src/event/manager/set_rect_mgr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
use super::Pending;
use crate::draw::DrawShared;
use crate::event::EventState;
use crate::geom::{Size, Vec2};
use crate::layout::Align;
use crate::geom::{Rect, Size, Vec2};
use crate::layout::{Align, AlignHints};
use crate::text::TextApi;
use crate::theme::{SizeHandle, SizeMgr, TextClass};
use crate::theme::{Feature, SizeMgr, TextClass, ThemeSize};
use crate::{TkAction, Widget, WidgetExt, WidgetId};
use std::ops::{Deref, DerefMut};

Expand All @@ -26,7 +26,7 @@ use crate::{event::Event, Layout};
/// `SetRectMgr` supports [`Deref`] and [`DerefMut`] with target [`EventState`].
#[must_use]
pub struct SetRectMgr<'a> {
sh: &'a dyn SizeHandle,
sh: &'a dyn ThemeSize,
ds: &'a mut dyn DrawShared,
pub(crate) ev: &'a mut EventState,
}
Expand All @@ -35,7 +35,7 @@ impl<'a> SetRectMgr<'a> {
/// Construct
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
pub fn new(sh: &'a dyn SizeHandle, ds: &'a mut dyn DrawShared, ev: &'a mut EventState) -> Self {
pub fn new(sh: &'a dyn ThemeSize, ds: &'a mut dyn DrawShared, ev: &'a mut EventState) -> Self {
SetRectMgr { sh, ds, ev }
}

Expand Down Expand Up @@ -81,6 +81,15 @@ impl<'a> SetRectMgr<'a> {
widget.configure(self);
}

/// Align a feature's rect
///
/// In case the input `rect` is larger than desired on either axis, it is
/// reduced in size and offset within the original `rect` as is preferred.
#[inline]
pub fn align_feature(&self, feature: Feature, rect: Rect, hints: AlignHints) -> Rect {
self.sh.align_feature(feature, rect, hints)
}

/// Update a text object, setting font properties and wrap size
///
/// Returns required size.
Expand Down
6 changes: 6 additions & 0 deletions crates/kas-core/src/layout/size_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ impl SizeRules {
}
}

/// A fixed size with given (symmetric) `margin`
#[inline]
pub fn fixed_splat(size: i32, margin: u16) -> Self {
Self::fixed(size, (margin, margin))
}

/// A fixed size, scaled from virtual pixels
///
/// This is a shortcut to [`SizeRules::fixed`] using virtual-pixel sizes
Expand Down
89 changes: 63 additions & 26 deletions crates/kas-core/src/theme/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

//! "Handle" types used by themes
use super::{FrameStyle, MarkStyle, SizeHandle, SizeMgr, TextClass};
use super::{FrameStyle, MarkStyle, SizeMgr, TextClass, ThemeSize};
use crate::dir::Direction;
use crate::draw::{color::Rgb, Draw, DrawShared, ImageId, PassType};
use crate::event::{EventState, SetRectMgr};
Expand Down Expand Up @@ -45,7 +45,7 @@ impl Default for Background {
/// - `draw.checkbox(&*self, self.state);` — note `&*self` to convert from to
/// `&W` from `&mut W`, since the latter would cause borrow conflicts
pub struct DrawMgr<'a> {
h: &'a mut dyn DrawHandle,
h: &'a mut dyn ThemeDraw,
id: WidgetId,
}

Expand Down Expand Up @@ -88,7 +88,7 @@ impl<'a> DrawMgr<'a> {
/// Construct from a [`DrawMgr`] and [`EventState`]
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
pub fn new(h: &'a mut dyn DrawHandle, id: WidgetId) -> Self {
pub fn new(h: &'a mut dyn ThemeDraw, id: WidgetId) -> Self {
DrawMgr { h, id }
}

Expand All @@ -104,14 +104,14 @@ impl<'a> DrawMgr<'a> {

/// Access a [`SetRectMgr`]
pub fn set_rect_mgr<F: FnMut(&mut SetRectMgr) -> T, T>(&mut self, mut f: F) -> T {
let (sh, ds, ev) = self.h.components();
let mut mgr = SetRectMgr::new(sh, ds, ev);
let (sh, draw, ev) = self.h.components();
let mut mgr = SetRectMgr::new(sh, draw.shared(), ev);
f(&mut mgr)
}

/// Access a [`DrawShared`]
pub fn draw_shared(&mut self) -> &mut dyn DrawShared {
self.h.components().1
self.h.components().1.shared()
}

/// Access the low-level draw device
Expand All @@ -120,17 +120,18 @@ impl<'a> DrawMgr<'a> {
/// base trait [`Draw`]. To access further functionality, it is necessary
/// to downcast with [`crate::draw::DrawIface::downcast_from`].
pub fn draw_device(&mut self) -> &mut dyn Draw {
self.h.draw_device()
self.h.components().1
}

/// Draw to a new pass
///
/// Adds a new draw pass for purposes of enforcing draw order. Content of
/// the new pass will be drawn after content in the parent pass.
pub fn with_pass<F: FnOnce(DrawMgr)>(&mut self, f: F) {
let clip_rect = self.h.get_clip_rect();
let id = self.id.clone();
self.h.new_pass(
self.h.get_clip_rect(),
clip_rect,
Offset::ZERO,
PassType::Clip,
Box::new(|h| f(DrawMgr { h, id })),
Expand Down Expand Up @@ -175,7 +176,7 @@ impl<'a> DrawMgr<'a> {
/// [clip region](Self::with_clip_region) or an
/// [overlay](Self::with_overlay). This may be used to cull hidden
/// items from lists inside a scrollable view.
pub fn get_clip_rect(&self) -> Rect {
pub fn get_clip_rect(&mut self) -> Rect {
self.h.get_clip_rect()
}

Expand Down Expand Up @@ -326,43 +327,79 @@ impl<'a> std::ops::BitOrAssign<TkAction> for DrawMgr<'a> {
}
}

/// A handle to the active theme, used for drawing
/// Theme drawing implementation
///
/// # Theme extension
///
/// Most themes will not want to implement *everything*, but rather derive
/// not-explicitly-implemented methods from a base theme. This may be achieved
/// with the [`kas_macros::extends`] macro:
/// ```ignore
/// #[extends(ThemeDraw, base = self.base())]
/// impl ThemeDraw {
/// // only implement some methods here
/// }
/// ```
/// Note: [`Self::components`] must be implemented
/// explicitly since this method returns references.
///
/// If Rust had stable specialization + GATs + negative trait bounds we could
/// allow theme extension without macros as follows.
/// <details>
///
/// ```ignore
/// #![feature(generic_associated_types)]
/// #![feature(specialization)]
/// # use kas_core::geom::Rect;
/// # use kas_core::theme::ThemeDraw;
/// /// Provides a default implementation of each theme method over a base theme
/// pub trait ThemeDrawExtends: ThemeDraw {
/// /// Type of base implementation
/// type Base<'a>: ThemeDraw where Self: 'a;
///
/// /// Access the base theme
/// fn base<'a>(&'a mut self) -> Self::Base<'a>;
/// }
///
/// // Note: we may need negative trait bounds here to avoid conflict with impl for Box<H>
/// impl<D: ThemeDrawExtends> ThemeDraw for D {
/// default fn get_clip_rect(&mut self) -> Rect {
/// self.base().get_clip_rect()
/// }
///
/// // And so on for other methods...
/// }
/// ```
/// </details>
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
#[autoimpl(for<H: trait + ?Sized> Box<H>)]
#[cfg_attr(feature = "stack_dst", autoimpl(
for<H: trait + ?Sized, S: Default + Copy + AsRef<[usize]> + AsMut<[usize]>>
stack_dst::ValueA<H, S>
))]
pub trait DrawHandle {
/// Access components: [`SizeHandle`], [`DrawShared`], [`EventState`]
fn components(&mut self) -> (&dyn SizeHandle, &mut dyn DrawShared, &mut EventState);

/// Access the low-level draw device
///
/// Note: this drawing API is modular, with limited functionality in the
/// base trait [`Draw`]. To access further functionality, it is necessary
/// to downcast with [`crate::draw::DrawIface::downcast_from`].
fn draw_device(&mut self) -> &mut dyn Draw;
pub trait ThemeDraw {
/// Access components: [`ThemeSize`], [`Draw`], [`EventState`]
fn components(&mut self) -> (&dyn ThemeSize, &mut dyn Draw, &mut EventState);

/// Construct a new pass
fn new_pass<'a>(
&mut self,
rect: Rect,
offset: Offset,
class: PassType,
f: Box<dyn FnOnce(&mut dyn DrawHandle) + 'a>,
f: Box<dyn FnOnce(&mut dyn ThemeDraw) + 'a>,
);

/// Target area for drawing
///
/// Drawing is restricted to this [`Rect`]. Affected by [`Self::new_pass`].
/// This may be used to cull hidden items from lists inside a scrollable view.
fn get_clip_rect(&self) -> Rect;
fn get_clip_rect(&mut self) -> Rect;

/// Draw a frame inside the given `rect`
///
/// The frame dimensions are given by [`SizeHandle::frame`].
/// The frame dimensions are given by [`ThemeSize::frame`].
fn frame(&mut self, id: &WidgetId, rect: Rect, style: FrameStyle, bg: Background);

/// Draw a separator in the given `rect`
Expand All @@ -383,7 +420,7 @@ pub trait DrawHandle {

/// Draw text with effects
///
/// [`DrawHandle::text`] already supports *font* effects: bold,
/// [`ThemeDraw::text`] already supports *font* effects: bold,
/// emphasis, text size. In addition, this method supports underline and
/// strikethrough effects.
///
Expand Down Expand Up @@ -478,8 +515,8 @@ pub trait DrawHandle {
mod test {
use super::*;

fn _draw_handle_ext(mut draw: DrawMgr) {
// We can't call this method without constructing an actual DrawHandle.
fn _draw_ext(mut draw: DrawMgr) {
// We can't call this method without constructing an actual ThemeDraw.
// But we don't need to: we just want to test that methods are callable.

let _scale = draw.size_mgr().scale_factor();
Expand Down
4 changes: 2 additions & 2 deletions crates/kas-core/src/theme/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ mod draw;
mod size;
mod style;

pub use draw::{Background, DrawHandle, DrawMgr};
pub use size::{SizeHandle, SizeMgr};
pub use draw::{Background, DrawMgr, ThemeDraw};
pub use size::{SizeMgr, ThemeSize};
pub use style::*;

#[allow(unused)]
Expand Down
Loading

0 comments on commit bb46d25

Please sign in to comment.