Skip to content

Commit

Permalink
Merge pull request #475 from kas-gui/work3
Browse files Browse the repository at this point in the history
Prefer taking EventState arg over returning Action
  • Loading branch information
dhardy authored Feb 9, 2025
2 parents 315ba8a + 9f6c62a commit cfa4e62
Show file tree
Hide file tree
Showing 25 changed files with 308 additions and 329 deletions.
73 changes: 1 addition & 72 deletions crates/kas-core/src/core/scroll_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use crate::event::EventCx;
use crate::geom::{Offset, Size};
use crate::{Action, Widget};
use crate::Widget;
#[allow(unused)] use crate::{Events, Layout};

/// Additional functionality on scrollable widgets
Expand Down Expand Up @@ -50,74 +50,3 @@ pub trait Scrollable: Widget {
/// resulting offset is returned.
fn set_scroll_offset(&mut self, cx: &mut EventCx, offset: Offset) -> Offset;
}

/// Scroll bar mode
///
/// Note that in addition to this mode, bars may be disabled on each axis.
#[kas_macros::impl_default(ScrollBarMode::Auto)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum ScrollBarMode {
/// Scroll bars are always shown if enabled.
Fixed,
/// Automatically enable/disable scroll bars as required when resized.
///
/// This has the side-effect of reserving enough space for scroll bars even
/// when not required.
Auto,
/// Scroll bars float over content and are only drawn when hovered over by
/// the mouse.
Invisible,
}

/// Scroll bar control
pub trait HasScrollBars {
/// Get mode
fn get_mode(&self) -> ScrollBarMode;

/// Set mode
fn set_mode(&mut self, mode: ScrollBarMode) -> Action;

/// Get currently visible bars
///
/// Returns `(horiz, vert)` tuple.
fn get_visible_bars(&self) -> (bool, bool);

/// Set enabled bars without adjusting mode
///
/// Note: if mode is `Auto` this has no effect.
///
/// This requires a [`Action::RESIZE`].
fn set_visible_bars(&mut self, bars: (bool, bool)) -> Action;

/// Set auto mode (inline)
#[inline]
fn with_auto_bars(mut self) -> Self
where
Self: Sized,
{
let _ = self.set_mode(ScrollBarMode::Auto);
self
}

/// Set fixed bars (inline)
#[inline]
fn with_fixed_bars(mut self, horiz: bool, vert: bool) -> Self
where
Self: Sized,
{
let _ = self.set_mode(ScrollBarMode::Fixed);
let _ = self.set_visible_bars((horiz, vert));
self
}

/// Set invisible bars (inline)
#[inline]
fn with_invisible_bars(mut self, horiz: bool, vert: bool) -> Self
where
Self: Sized,
{
let _ = self.set_mode(ScrollBarMode::Invisible);
let _ = self.set_visible_bars((horiz, vert));
self
}
}
3 changes: 1 addition & 2 deletions crates/kas-core/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,4 @@ pub use crate::{widget, widget_index, widget_set_rect};
#[doc(no_inline)]
pub use crate::{Events, Layout, Tile, TileExt, Widget, Window, WindowCommand};
#[doc(no_inline)] pub use crate::{HasId, Id};
#[doc(no_inline)]
pub use crate::{HasScrollBars, Node, ScrollBarMode, Scrollable};
#[doc(no_inline)] pub use crate::{Node, Scrollable};
3 changes: 1 addition & 2 deletions crates/kas-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub fn impl_default(attr: TokenStream, item: TokenStream) -> TokenStream {
#[proc_macro_error]
pub fn autoimpl(attr: TokenStream, item: TokenStream) -> TokenStream {
use autoimpl::ImplTrait;
use scroll_traits::{ImplHasScrollBars, ImplScrollable};
use scroll_traits::ImplScrollable;
use std::iter::once;

let mut toks = item.clone();
Expand All @@ -84,7 +84,6 @@ pub fn autoimpl(attr: TokenStream, item: TokenStream) -> TokenStream {
autoimpl::STD_IMPLS
.iter()
.cloned()
.chain(once(&ImplHasScrollBars as &dyn ImplTrait))
.chain(once(&ImplScrollable as &dyn ImplTrait))
.find(|impl_| impl_.path().matches_ident_or_path(path))
};
Expand Down
41 changes: 0 additions & 41 deletions crates/kas-macros/src/scroll_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,44 +53,3 @@ impl ImplTrait for ImplScrollable {
}
}
}

pub struct ImplHasScrollBars;
impl ImplTrait for ImplHasScrollBars {
fn path(&self) -> SimplePath {
SimplePath::new(&["", "kas", "HasScrollBars"])
}

fn support_ignore(&self) -> bool {
false
}

fn support_using(&self) -> bool {
true
}

fn struct_items(&self, _: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> {
if let Some(using) = args.using_member() {
let methods = quote! {
#[inline]
fn get_mode(&self) -> ::kas::ScrollBarMode {
self.#using.get_mode()
}
#[inline]
fn set_mode(&mut self, mode: ::kas::ScrollBarMode) -> ::kas::Action {
self.#using.set_mode(mode)
}
#[inline]
fn get_visible_bars(&self) -> (bool, bool) {
self.#using.get_visible_bars()
}
#[inline]
fn set_visible_bars(&mut self, bars: (bool, bool)) -> ::kas::Action {
self.#using.set_visible_bars(bars)
}
};
Ok((quote! { ::kas::HasScrollBars }, methods))
} else {
Err(Error::RequireUsing)
}
}
}
4 changes: 2 additions & 2 deletions crates/kas-resvg/src/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,10 @@ impl_scope! {
/// Default size is 128 × 128; default stretch is [`Stretch::High`].
/// Other fields use [`PixmapScaling`]'s default values.
#[inline]
pub fn set_scaling(&mut self, f: impl FnOnce(&mut PixmapScaling)) -> Action {
pub fn set_scaling(&mut self, cx: &mut EventState, f: impl FnOnce(&mut PixmapScaling)) {
f(&mut self.scaling);
// NOTE: if only `aspect` is changed, REDRAW is enough
Action::RESIZE
cx.resize(self);
}
}

Expand Down
41 changes: 22 additions & 19 deletions crates/kas-resvg/src/svg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,30 +153,31 @@ impl_scope! {
pub fn new(data: &'static [u8]) -> Result<Self, impl std::error::Error> {
let mut svg = Svg::default();
let source = Source::Static(data, None);
svg.load_source(source).map(|_action| svg)
svg.load_source(source).map(|_| svg)
}

/// Construct from a path
pub fn new_path<P: AsRef<Path>>(path: P) -> Result<Self, impl std::error::Error> {
let mut svg = Svg::default();
let _action = svg.load_path_(path.as_ref())?;
svg._load_path(path.as_ref())?;
Result::<Self, LoadError>::Ok(svg)
}

/// Load from `data`
///
/// Replaces existing data, but does not re-render until a resize
/// happens (hence returning [`Action::RESIZE`]).
///
/// This sets [`PixmapScaling::size`] from the SVG.
pub fn load(&mut self, data: &'static [u8], resources_dir: Option<&Path>)
-> Result<Action, impl std::error::Error>
{
/// Replaces existing data and request a resize. The sizing policy is
/// set to [`PixmapScaling::size`] using dimensions from the SVG.
pub fn load(
&mut self,
cx: &mut EventState,
data: &'static [u8],
resources_dir: Option<&Path>,
) -> Result<(), impl std::error::Error> {
let source = Source::Static(data, resources_dir.map(|p| p.to_owned()));
self.load_source(source)
self.load_source(source).map(|_| cx.resize(self))
}

fn load_source(&mut self, source: Source) -> Result<Action, usvg::Error> {
fn load_source(&mut self, source: Source) -> Result<(), usvg::Error> {
// Set scaling size. TODO: this is useless if Self::with_size is called after.
let size = source.tree()?.size();
self.scaling.size = LogicalSize(size.width(), size.height());
Expand All @@ -185,19 +186,21 @@ impl_scope! {
State::Ready(_, px) => State::Ready(source, px),
_ => State::Initial(source),
};
Ok(Action::RESIZE)
Ok(())
}

/// Load from a path
///
/// This is a wrapper around [`Self::load`].
pub fn load_path<P: AsRef<Path>>(&mut self, path: P)
-> Result<Action, impl std::error::Error>
{
self.load_path_(path.as_ref())
pub fn load_path<P: AsRef<Path>>(
&mut self,
cx: &mut EventState,
path: P,
) -> Result<(), impl std::error::Error> {
self._load_path(path.as_ref()).map(|_| cx.resize(self))
}

fn load_path_(&mut self, path: &Path) -> Result<Action, LoadError> {
fn _load_path(&mut self, path: &Path) -> Result<(), LoadError> {
let buf = std::fs::read(path)?;
let rd = path.parent().map(|path| path.to_owned());
let source = Source::Heap(buf.into(), rd);
Expand Down Expand Up @@ -231,10 +234,10 @@ impl_scope! {
/// [`PixmapScaling::size`] is set from the SVG on loading (it may also be set here).
/// Other scaling parameters take their default values from [`PixmapScaling`].
#[inline]
pub fn set_scaling(&mut self, f: impl FnOnce(&mut PixmapScaling)) -> Action {
pub fn set_scaling(&mut self, cx: &mut EventState, f: impl FnOnce(&mut PixmapScaling)) {
f(&mut self.scaling);
// NOTE: if only `aspect` is changed, REDRAW is enough
Action::RESIZE
cx.resize(self);
}
}

Expand Down
64 changes: 30 additions & 34 deletions crates/kas-view/src/list_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,11 @@ impl_scope! {
}
impl<A: ListData, V: Driver<A::Item, A>> ListView<A, V, Direction> {
/// Set the direction of contents
pub fn set_direction(&mut self, direction: Direction) -> Action {
if direction == self.direction {
return Action::empty();
pub fn set_direction(&mut self, cx: &mut EventState, direction: Direction) {
if direction != self.direction {
self.direction = direction;
cx.action(self, Action::SET_RECT);
}

self.direction = direction;
Action::SET_RECT
}
}

Expand Down Expand Up @@ -168,28 +166,29 @@ impl_scope! {
/// except via [`Select`] from view widgets. (TODO: reconsider this.)
///
/// [`Select`]: kas::messages::Select
pub fn set_selection_mode(&mut self, mode: SelectionMode) -> Action {
pub fn set_selection_mode(&mut self, cx: &mut EventState, mode: SelectionMode) {
self.sel_mode = mode;
match mode {
SelectionMode::None if !self.selection.is_empty() => {
self.selection.clear();
Action::REDRAW
cx.redraw(self);
}
SelectionMode::Single if self.selection.len() > 1 => {
if let Some(first) = self.selection.iter().next().cloned() {
self.selection.retain(|item| *item == first);
}
Action::REDRAW
cx.redraw(self);
}
_ => Action::empty(),
_ => (),
}
}
/// Set the selection mode (inline)
/// Set the initial selection mode (inline)
///
/// See [`Self::set_selection_mode`] documentation.
#[must_use]
pub fn with_selection_mode(mut self, mode: SelectionMode) -> Self {
let _ = self.set_selection_mode(mode);
debug_assert!(self.selection.is_empty());
self.sel_mode = mode;
self
}

Expand All @@ -201,14 +200,11 @@ impl_scope! {
///
/// By default, [`SelectionStyle::Highlight`] is used. Other modes may
/// add margin between elements.
pub fn set_selection_style(&mut self, style: SelectionStyle) -> Action {
let action = if style.is_external() != self.sel_style.is_external() {
Action::RESIZE
} else {
Action::empty()
pub fn set_selection_style(&mut self, cx: &mut EventState, style: SelectionStyle) {
if style.is_external() != self.sel_style.is_external() {
cx.resize(&self);
};
self.sel_style = style;
action
}
/// Set the selection style (inline)
///
Expand All @@ -233,12 +229,10 @@ impl_scope! {
}

/// Clear all selected items
pub fn clear_selected(&mut self) -> Action {
if self.selection.is_empty() {
Action::empty()
} else {
pub fn clear_selected(&mut self, cx: &mut EventState) {
if !self.selection.is_empty() {
self.selection.clear();
Action::REDRAW
cx.redraw(self);
}
}

Expand All @@ -248,30 +242,32 @@ impl_scope! {
/// Does not verify the validity of `key`.
/// Does not send [`SelectionMsg`] messages.
///
/// Returns `Action::REDRAW` if newly selected, `Action::empty()` if
/// Returns `true` if newly selected, `false` if
/// already selected. Fails if selection mode does not permit selection
/// or if the key is invalid.
pub fn select(&mut self, key: A::Key) -> Action {
pub fn select(&mut self, cx: &mut EventState, key: A::Key) -> bool {
match self.sel_mode {
SelectionMode::None => return Action::empty(),
SelectionMode::None => return false,
SelectionMode::Single => self.selection.clear(),
_ => (),
}
match self.selection.insert(key) {
true => Action::REDRAW,
false => Action::empty(),
let r = self.selection.insert(key);
if r {
cx.redraw(self);
}
r
}

/// Directly deselect an item
///
/// Returns `Action::REDRAW` if deselected, `Action::empty()` if not
/// Returns `true` if deselected, `false` if not
/// previously selected or if the key is invalid.
pub fn deselect(&mut self, key: &A::Key) -> Action {
match self.selection.remove(key) {
true => Action::REDRAW,
false => Action::empty(),
pub fn deselect(&mut self, cx: &mut EventState, key: &A::Key) -> bool {
let r = self.selection.remove(key);
if r {
cx.redraw(self);
}
r
}

/// Get the direction of contents
Expand Down
Loading

0 comments on commit cfa4e62

Please sign in to comment.