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

Const (non-mut) widget draw #466

Open
dhardy opened this issue Jan 16, 2025 · 0 comments
Open

Const (non-mut) widget draw #466

dhardy opened this issue Jan 16, 2025 · 0 comments

Comments

@dhardy
Copy link
Collaborator

dhardy commented Jan 16, 2025

This is an annoying issue, possibly best left until mut generics is available.

If nothing else, this serves as an exemplar of what we might want mut generics to achieve.

Problem

fn Layout::draw (i.e. widget draw) takes &mut self. Implementations only require &self, except those using a layout visitor. (The same is true of fn try_probe while fn size_rules and fn set_rect impls do require &mut self.)

Background

Macro-generated layout implementations actually implement this trait:

pub trait LayoutVisitor {
    /// Layout defined by a [`Visitor`]
    fn layout_visitor(&mut self) -> Visitor<impl Visitable>;
}

which uses these:

/// A sub-set of [`Layout`] used by [`Visitor`].
///
/// Unlike when implementing a widget, all methods of this trait must be
/// implemented directly.
#[crate::autoimpl(for<T: trait + ?Sized> &'_ mut T, Box<T>)]
pub trait Visitable {
    /// Get size rules for the given axis
    ///
    /// This method is identical to [`Layout::size_rules`].
    fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules;

    /// Set size and position
    ///
    /// The caller is expected to set `self.core.rect = rect;`.
    /// In other respects, this functions identically to [`Layout::set_rect`].
    fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints);

    /// Look for a widget at this `coord`
    ///
    /// Returns the [`Id`] of a child when some child occupies `coord`. Returns
    /// [`None`] when there is no (probable) child widget at `coord`, in which
    /// case the caller may use its own [`Id`].
    fn try_probe(&mut self, coord: Coord) -> Option<Id>;

    /// Draw a widget and its children
    ///
    /// This method is identical to [`Layout::draw`].
    fn draw(&mut self, draw: DrawCx);
}

/// A layout visitor
///
/// Objects are generated by [`layout`] syntax. These all have limited lifetime.
///
/// [`layout`]: crate::widget#layout-1
pub struct Visitor<V: Visitable>(V);

The actual implementation of fn Layout::draw is then just LayoutVisitor::layout_visitor(self).draw(draw).

Solutions

The first solution is to simply ignore the problem: fn draw receives &mut self even though it doesn't "need" it. This is the simplest (and perhaps best) option.

The "obvious" alternative is to let fn draw and fn try_probe receive &self, but this is not trivial to implement. Details below.

A const layout visitor

fn LayoutVisitor::layout_visitor must take &mut self in order to support fn Visitable::set_rect (and fn size_rules). If we wanted to support const (non-mut) usage, we'd need another method, fn layout_visitor_const(&self). Since this method's return value cannot implement fn Visitable::set_rect (which requires &mut self), we'd need to split the Visitable trait.

A possible result of this is:

#[crate::autoimpl(for<T: trait + ?Sized> &'_ T, Box<T>)]
pub trait VisitableConst {
    fn try_probe(&self, coord: Coord) -> Option<Id>;

    fn draw(&self, draw: DrawCx);
}

#[crate::autoimpl(for<T: trait + ?Sized> &'_ mut T, Box<T>)]
pub trait Visitable: VisitableConst {
    /// Get size rules for the given axis
    ///
    /// This method is identical to [`Layout::size_rules`].
    fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules;

    /// Set size and position
    ///
    /// The caller is expected to set `self.core.rect = rect;`.
    /// In other respects, this functions identically to [`Layout::set_rect`].
    fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints);
}

pub struct Visitor<V: VisitableConst>(V);

pub trait LayoutVisitor {
    fn layout_visitor_const(&self) -> Visitor<impl VisitableConst>;

    fn layout_visitor(&mut self) -> Visitor<impl Visitable>;
}

This approach is probably viable, though now all implementations of LayoutVisitor must write impls for both fn layout_visitor and fn layout_visitor_const despite the impls being mostly identical. mut-generics should allow these to collapse down into a single impl, assuming the generics also apply to the trait Visitable.

const Visitable

We described trait VisitableConst above, which is required to support implementations of fn layout_visitor_const. In practice, this is just an artifact required by the type system. We would never want to impl VisitableConst without implementing Visitable (mut).

It would be neater if we could simply write const Visitable (or maybe Visitable<const>).

Const visitors

Visitor<impl Visitable> objects are provided by visitor methods, e.g. fn Visitor::single(widget: &'a mut dyn Layout) returns Visitor(Single { .. }) using

struct Single<'a> {
    widget: &'a mut dyn Layout,
}

impl<'a> Visitable for Single<'a> { /* ... */ }

For the above, we'd need a const alternative, fn single_const(widget: &'a dyn Layout).

The implementation object could remain the same struct Single type, if its generics change to Single<W> { widget: W }, however it would now need at least two, possibly three impl blocks (VisitableConst for Single<&'a dyn Layout>, VisitableConst for Single<&'a mut dyn Layout, Visitable for Single<&'a mut dyn Layout>) instead of just one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant