Commit Graph

59 Commits

Author SHA1 Message Date
Olivier FAURE fad997afa5
Add Properties to Button (#892)
Depends on #904.

Add BackgroundColor, BorderColor, BorderWidth, CornerRadius and Padding
properties.
Modularize button painting code.
Paint border after background to get slightly better border appearance.
Update screenshots.
Add screenshot test using properties.
2025-03-29 09:43:22 +00:00
Daniel McNab d4ad901fc6
Virtual Scrolling Widget (#882)
This is a minimum viable virtual scrolling widget, making the following
assumptions:
-  It does not *care* about scrollbars. This has several big advantages:
    - It allows for "infinite" virtual up and down-scrolling
- It allows for much more control by users of how the scrolling happens
(e.g. a date based scrollbar)

It does *not* assume that each item has a fixed height, and instead uses
some best-effort heuristics to work out which items should be loaded.

We've done some thinking about how to handle arbitrary transforms of
child widgets. Essentially, the only way we can reasonably handle this
is for each child widget to be automatically clipped to their layout
rectangle (...maybe e.g. 20% larger than it; that's hard because if we
don't use fixed margins then the variable scroll controller needs to
know about it).

This also does not handle focus and other details around (e.g.)
textboxes properly; it's not actually possible for the VirtualScroll
widget to know which child has the focus, so it can't keep that widget
around. Saving the textbox state would be nice, but requires a redesign
of a lot of things, essentially.

![A screenshot of fizzbuzz starting at 'FizzBuzz' at the top of the
screen, followed by 42031, going to
42106.](https://github.com/user-attachments/assets/fe55be05-ad07-4dc7-b7c2-c9209f5a114b)

Fixes #51

---------

Co-authored-by: Bruce Mitchener <bruce.mitchener@gmail.com>
2025-03-28 09:48:00 +00:00
Olivier FAURE 3a21b21bbf
Mark `masonry/screenshots/*.png` as LFS files (#911)
These files we previously stored as blobs as-is. This stores them using
the same LFS method as other screenshots.
2025-03-25 13:32:17 +00:00
Olivier FAURE 8002eecbcd
Tighten screenshot testing (#904)
Remove nv-flip dependency and use pixel-by-pixel comparison instead.
Reduce error tolerance in tests.
Update a few outdated screenshots.

Create `assert_failing_render_snapshot` macro to make sure our diffing
infrastructure catches regressions.

Fixes #893.
2025-03-23 19:19:25 +00:00
Olivier FAURE 8d322b63a8
Change how some widgets are rendered (#886)
Update the code for Button, Textbox and ProgressBar.

Make the border and background disjoint.
Correctly interpret the border width.
2025-03-17 14:22:46 +00:00
Matt Campbell 711135027f
Update to parley 0.3 (#883)
This update requires us to handle one breaking API change in parley.
2025-03-10 17:43:42 +00:00
Olivier FAURE 60c037dc1f
Add arbitrary properties to widgets (#873)
Add `Properties[Mut]` argument to widget methods.
Add a third TreeArena to sort per-widget arbitrary property values.

For the type-to-value map, I considered the following crates:
- https://docs.rs/typemap/latest/typemap/
- https://crates.io/crates/typemap_rev
- https://crates.io/crates/typemap-ors
- https://github.com/chris-morgan/anymap
- https://github.com/reivilibre/anymap3

Of these, anymap3 is the only one actively maintained (last commit less
than 12 months ago). The code source itself is extremely light and
simple; we may or may not decide to roll out our own implementation down
the line.

Add a BackgroundBrush property used by SizedBox as a proof of concept.

Note that SizedBox still has its `background` field, but we should
expect future widgets to use almost *exclusively* properties; properties
usually shouldn't be redundant with local fields.

To get there, we'll first need to better integrate properties in Xilem.
2025-03-06 11:32:06 +00:00
Olivier FAURE bc8e9fecd2
Switch to Edition 2024 (#880)
I've ignored most of the changes and the projects still compile and test
fine. Changes I did commit:

Add `+ use<>` after functions.
Remove `ref mut` from some patterns.
Bump MSRV.
Apply new rustfmt format.
2025-03-05 13:15:47 +00:00
Bruce Mitchener f63e7096dd
Update to current `parley` `main` (#870)
This updates to the version using the updated objc2 and binding crates
for Fontique.

It also has a minor API change.
2025-02-19 02:50:26 +00:00
Olivier FAURE d77b31b892
Reorganize Masonry modules (#848) 2025-01-24 11:24:31 +00:00
Olivier FAURE df3107e1d4
Add screenshots to documentation (#832)
Create include_screenshot macro
2025-01-23 18:21:53 +00:00
Olivier FAURE df1efa472b
Add tests to Masonry's examples (#844) 2025-01-22 11:18:29 +00:00
Bruce Mitchener 46170faddb
Update to Vello 0.4, Peniko 0.3.1, Color 0.2.3 (#840)
This also lets us update to using `Color::from_rgb8()` when there is no
need for alpha to be specified.
2025-01-21 09:13:07 +00:00
Olivier FAURE 320159b669
Apply is/has naming convention for flags and methods (#831)
Rename has_pointer_capture to is_pointer_capture_target.
Rename has_focus to has_focus_target.
Rename is_focused to is_focus_target.
Add has_hovered flag.
Add ChildHoverChanged event.
2025-01-16 15:17:39 +00:00
Olivier FAURE c1e0c83b0b
Have AppDriver take a reference to RenderRoot for more flexibility (#808) 2025-01-10 14:16:37 +00:00
Olivier FAURE 6da98ae5ef
Remove redundant code in calc_masonry example (#807)
Fixes #796
2025-01-10 14:09:24 +00:00
Bruce Mitchener c1ef637309
Use `palette` and `Color` from top level Xilem and Masonry (#800) 2024-12-20 16:34:11 +00:00
Bruce Mitchener adcbafbd8e
Update to git Parley and Vello. (#798)
This brings the new releases of Color and Peniko and start making sure
things are good for the upcoming releases of Parley and Vello.
2024-12-20 14:52:05 +00:00
Bruce Mitchener 653e424a8e
Consistently use `FontWeight` as the name (#778)
Right now, there are uses of `Weight` from Fontique, `TextWeight` from
Masonry, and `FontWeight` from Parley.

We should just use a single name.
2024-12-09 10:10:11 +00:00
Daniel McNab d8d916c2d8
Unify Prose and Textbox with an underlying `TextRegion` (#754)
Remaining steps

- [x] Update Textbox to use this
- [x] Add internal padding

Follow-up work:
- [ ] Restore IME support
- [ ] Upstream new result of PlainEditor (once other steps are complete)
2024-11-21 14:35:01 +00:00
Daniel McNab 10dc9d171c
Update to Vello 0.3.0, Parley main, AccessKit 0.17 (#616)
The biggest remaining issue in this PR is that IME support is not
present.

However, I think landing this is *better* than not landing it, because:
1) If we don't land it, it's going to languish again
2) Getting IME support back can be parallelised (cc @tomcur)
3) Getting Vello 0.3.0 and Parley 0.2.0 unlocks real advantages,
including full emoji support (#420).

To be clear, my first follow-up priority will be connecting the IME back
up. I do not however think this should block on Parley 0.3.0.

Discussion in
https://xi.zulipchat.com/#narrow/channel/317477-masonry/topic/Updating.20Parley.20dependency
2024-11-18 17:25:02 +00:00
Daniel McNab a6800f490f
Include widget ids in tracing spans (#729)
New tracy image:


![image](https://github.com/user-attachments/assets/94e54c89-8159-4dd4-a521-4a7122f64375)

New log tracing file:
<details><summary>An overview of the new logs</summary>
<p>

```
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}: masonry::passes::update: RootWidget received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}: masonry::passes::update: SizedBox received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}:Flex{id=#6}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}:Flex{id=#6}:Flex{id=#3}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}:Flex{id=#6}:Flex{id=#3}:Prose{id=#1}: masonry::passes::update: Prose received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}:Flex{id=#6}:Flex{id=#3}:Label{id=#2}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}:Flex{id=#6}:Flex{id=#5}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}:Flex{id=#6}:Flex{id=#5}:VariableLabel{id=#4}: masonry::passes::update: VariableLabel received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#10}: masonry::passes::update: Button received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#10}:Label{id=#9}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#12}: masonry::passes::update: Button received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#12}:Label{id=#11}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#14}: masonry::passes::update: Button received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#14}:Label{id=#13}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#16}: masonry::passes::update: Button received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#16}:Label{id=#15}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}: masonry::passes::update: Portal received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}: masonry::passes::update: SizedBox received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}:Flex{id=#23}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}:Flex{id=#23}:Flex{id=#20}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}:Flex{id=#23}:Flex{id=#20}:Prose{id=#18}: masonry::passes::update: Prose received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}:Flex{id=#23}:Flex{id=#20}:Label{id=#19}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}:Flex{id=#23}:Flex{id=#22}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}:Flex{id=#23}:Flex{id=#22}:VariableLabel{id=#21}: masonry::passes::update: VariableLabel received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#31}: masonry::passes::update: SizedBox received Update::WidgetAdded
```

</p>
</details> 

This was originally an experiment into caching spans, but I determined
that was non-viable due to the pass names.
2024-11-11 09:01:43 +00:00
Daniel McNab 18a6805ddc
Scope exceptions to the Linebender lint set (#730)
This allows exceptions to be burned down on a per-crate basis, rather
than needing to be addressed across the whole codebase at once.

Additionally, this now follows completely the Linebender lint set,
except for the unexpected-cfgs. I don't think I've introduced any
behaviour changes in this PR.
2024-11-09 08:37:56 +00:00
Bruce Mitchener 231e304249
masonry example: Fix reference to `on_status_changed` (#719)
This is now just `update`.
2024-10-29 16:40:35 +00:00
Olivier FAURE f322894e50
Replace WidgetMut methods with free functions. (#705)
See discussion here https://github.com/linebender/xilem/pull/663 and [on
zulip](https://xi.zulipchat.com/#narrow/channel/317477-masonry/topic/Improving.20docs.20for.20WidgetMut).

Basically, previous code was using `WidgetMut<MyWidget>` as a receiver.
This can be done when WidgetMut is a local type, but external users
wouldn't be able to do it. The result would be that users importing
Masonry as a dependency but copy-pasting code from one of our widgets
would get compile errors.

This PR switches to a syntax that external crates will be able to use
when declaring widgets. It's a more verbose, less readable syntax, but
it's unambiguous and doesn't require clever tricks.

We can consider switching back to WidgetMut-as-a-receiver when
`#![feature(arbitrary_self_types)]` or some equivalent gets stabilized.
See
https://github.com/rust-lang/rust/issues/44874#issuecomment-2122179688
for current progress.
2024-10-22 13:36:48 +00:00
Olivier FAURE ed5c141701
Reorganize top-level Masonry modules (#703) 2024-10-21 12:18:30 +00:00
Olivier FAURE 4d20e0dd19
Merge StatusChange with Update (#697) 2024-10-20 17:15:18 +00:00
Olivier FAURE dc9a2e2a97
Rename lifecycle to update (#677) 2024-10-17 12:09:14 +00:00
Olivier FAURE f4a8b47587
Refactor safety rails for layout pass (#644)
Merge call_widget_method_with_checks, run_layout_inner and
run_layout_on.
Remove needs_visit flag.
Add checks for invalid values in place_child, set_child_translation and
run_layout.

---------

Co-authored-by: jaredoconnell <jared.oc321@gmail.com>
2024-10-15 16:25:46 +00:00
Olivier FAURE 9a6178b3d4
Add request_render context method (#660)
Remove some redundant request_paint calls

Fixes #585.
2024-10-15 16:18:47 +00:00
Marco Melorio 634afd7010
Expose font weight parameter in label widget (#669) 2024-10-14 17:01:53 +00:00
Olivier FAURE a26bf11119
Add target field to EventCtx (#657)
Remove target field from AccessEvent
2024-10-13 01:21:52 +00:00
Olivier FAURE f6ad804ae6
Remove last mentions of hot state from code (#654) 2024-10-08 17:12:13 +00:00
Olivier FAURE f195c318e3
Rename RootWidget::get_element to RootWidget::child_mut (#645) 2024-10-07 20:10:25 +00:00
Olivier FAURE a04649dbc9
Rearrange text-handling code (#624)
Remove obsolete types.
Remove text_helpers module.
2024-10-03 15:05:35 +00:00
failingprovince fe140afc9a
feat(#574): reimplementation of `Image` widget `layout` function (#605)
Reimplement calculation of image size in `Image` widget `layout`
function.
Makes it match against `self.fill` before setting size.

Tests still missing.

Fixes #574
2024-10-02 10:08:00 +00:00
Matt Campbell 997da0314b
Remove `current_node` from `AccessCtx` (#614)
It makes more sense to pass the `NodeBuilder` reference separately to
the `accessibility` method. In particular, this avoids the need to keep
calling a method on `AccessCtx` to get that mutable reference.
2024-09-26 18:13:56 +00:00
Olivier FAURE 2642a9e146
Replace RouteWidgetAdded event with new register_children method (#602)
Make lifecycle method optional.
Remove InternalLifecycle.
2024-09-23 08:25:32 +00:00
Olivier FAURE 09d9ad555d
Make `Textbox` focusable and trigger redraw in response to `request_layout` (#537)
Fixes #301.
2024-09-16 15:31:37 +00:00
Jared O'Connell 3726e91a48
Grid layout (#570)
This PR adds a basic grid layout to Masonry and Xilem.

The way this layout works is it has a fixed grid based on the initial
size passed in, and grid items are placed based on the position
requested. Grid items are allowed to span more than one cell, if
requested.

There are potential improvements that could be done, like the use of
intrinsic sizing for varied column width based on content size. Though
that could be done in the future taffy layout if we want to keep this
one simple.


~~This PR is still a draft because of one remaining concern. I was not
able to successfully optimize with conditional calls to child widgets
for layout. It led to crashes about the paint rects not being within the
widget's paint rect. `Error in 'Grid' #16: paint_rect Rect { x0: 0.0,
y0: 0.0, x1: 800.0, y1: 610.0 } doesn't contain paint_rect Rect { x0:
400.5, y0: 0.0, x1: 800.5, y1: 150.0 } of child widget 'Button' #5`. My
failed attempt at fixing it is commented out.~~

Since I am rusty on View Sequences, a lot of that code is based on the
Flex implementation. Let me know if I did anything incorrectly or if any
of it is unnecessary, or if anything is missing.

---------

Co-authored-by: Daniel McNab <36049421+DJMcNab@users.noreply.github.com>
2024-09-11 14:55:19 +00:00
Tom Churchman 9a3c8e308c
masonry: replace `set_active` and `is_active` with pointer capture (#564)
Also improve documentation of pointer capture.

Continuation of 59ee615651
(https://github.com/linebender/xilem/pull/488).

Makes `has_pointer_capture` available on all context types except
`LayoutCtx`, like `is_active` used to be.
2024-09-10 11:55:51 +00:00
Olivier FAURE dcea01a4a9
Migrate layout pass (#529) 2024-09-09 12:35:36 +00:00
Tom Churchman 923c0fb8ca
Remove manual recursion to child `paint` and `accessibility` (#557)
Recursing is done inside the paint and accessibility passes since
ff7635e4c2. I believe this is the correct
continuation of #522, with the removal of these methods "left for later"
as mentioned in
https://github.com/linebender/xilem/pull/522#issuecomment-2298610203.

One note is that Flex now debug-paints its baseline under its children,
rather than over them.
2024-08-28 13:41:36 +00:00
Bruce Mitchener 7bd572b9a3
Fix typos. (#556) 2024-08-26 13:55:55 +00:00
Olivier FAURE 42a6a320da
Fix visual bug in masonry_calc example (#526) 2024-08-19 15:13:26 +00:00
Bruce Mitchener 052ac39667
masonry: Use Kurbo via Vello. (#223)
Rather than have to keep a dependency on `kurbo` at the correct version,
always use it via `vello`.

This makes this match how `peniko` is already used within `masonry`.
2024-08-16 13:53:09 +00:00
Olivier FAURE e0d6a309d1
Implement mutate pass from pass spec RFC (#510)
This is part of the "Pass Specification" RFC:
https://github.com/linebender/rfcs/pull/7

Rename WidgetCtx to MutateCtx.
Add a mutate pass.
Add a `mutate_later` context method to trigger that pass.
Refactor `edit_root_widget` to use a version of that pass.
Add a separate constructor for the synthetic WidgetState created in
RenderRoot.

---------

Co-authored-by: Philipp Mildenberger <philipp@mildenberger.me>
Co-authored-by: Daniel McNab <36049421+DJMcNab@users.noreply.github.com>
2024-08-14 20:06:01 +00:00
Olivier FAURE 59ee615651
Implement event and update_pointer passes from pass spec RFC (#488)
This is a first step in implementing the "Pass Specification" RFC:
https://github.com/linebender/rfcs/pull/7

Create a `passes` module.
Create event passes.
Create the update_pointer pass.

Remove `WidgetPod::update_hot_state` method.
Move mouse-cursor-handling code to update_pointer pass.
Implement pointer capture.
Refactor the TreeArena code.

---------

Co-authored-by: Daniel McNab <36049421+DJMcNab@users.noreply.github.com>
2024-08-13 08:52:27 +00:00
Jared O'Connell 759d746653
Xilem Calculator Example (#467)
This pull request adds a simple calculator that can display binomial
math (ex: 2 + 2 = 4). Math operations with more than two operands are
done by moving the prior binomial results into the left operands.

This pull request also follows the existing convention by masonry's
calculator example to calc_masonry to differentiate them.

Let me know if there are any changes that I should make to improve code
quality.

<img width="404" alt="image"
src="https://github.com/user-attachments/assets/3498913b-dd4d-451c-8fa2-fcd2f7fd26af">

---------

Co-authored-by: Daniel McNab <36049421+DJMcNab@users.noreply.github.com>
2024-07-31 15:07:55 +00:00
Daniel McNab 2dc1c08b80
Add a CSS `gap` inspired Flex property. (#437)
See https://developer.mozilla.org/en-US/docs/Web/CSS/gap

Since #428, we no longer have default spacing between flex elements in
Masonry. This makes the examples quite ugly.

Choices made based on [#xilem > Porting Taffy Integration to New
Xilem](https://xi.zulipchat.com/#narrow/stream/354396-xilem/topic/Porting.20Taffy.20Integration.20to.20New.20Xilem).
Of particular note is the choice to not have this be overwritten by
spacers, due to:

> My first thought is that users are going to be very confused when they
add a space between two items and the result is the items are closer
together.

There is no such concept of a spacer in CSS parlance
2024-07-19 14:14:38 +00:00