Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefer taking EventState arg over returning Action #475

Merged
merged 12 commits into from
Feb 9, 2025
Merged
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