From 9b2ebfc15783a7ea1e159489ff1655b8c9417902 Mon Sep 17 00:00:00 2001 From: Olivier FAURE Date: Mon, 22 Apr 2024 11:41:35 +0200 Subject: [PATCH] Import Masonry crate (#203) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds the `masonry` crate as-is to the repository. It doesn't integrate Xilem with Masonry in any way. Masonry is a modular backend providing a Widget tree for GUI libraries, originally forked from the Druid project. Previous history of this code is maintained in [the original Masonry repository](https://github.com/linebender/masonry). Explanations for design decisions may also be found in issues from [the Druid repository](https://github.com/linebender/druid). --------- Co-authored-by: 7k5x <7k5xlp0onfire@gmail.com> Co-authored-by: 8176135 <8176135@gmail.com> Co-authored-by: Aaron Muir Hamilton Co-authored-by: Alberto Slavica <104838972+albertoslavicadev@users.noreply.github.com> Co-authored-by: Al M Co-authored-by: Alve™ Co-authored-by: Andrea Cognolato Co-authored-by: Andrew Hickman Co-authored-by: Andrey Kabylin Co-authored-by: Andrii Lysenko Co-authored-by: Andrii Zymohliad Co-authored-by: Andrii Zymohliad Co-authored-by: Ankur Sethi Co-authored-by: Anna Scholtz Co-authored-by: Arif Driessen Co-authored-by: Arpad Borsos Co-authored-by: arthmis Co-authored-by: astrale-sharp <53686698+astrale-sharp@users.noreply.github.com> Co-authored-by: becky Co-authored-by: Benedikt Terhechte Co-authored-by: Benjamin Saunders Co-authored-by: binomial0 Co-authored-by: bitec0de <102129407+bitec0de@users.noreply.github.com> Co-authored-by: bobtwinkles Co-authored-by: bobtwinkles Co-authored-by: Brook Heisler Co-authored-by: Brook Heisler Co-authored-by: Bruce Mitchener Co-authored-by: Bruno Dupuis Co-authored-by: Calle Malmz Co-authored-by: Carl Malmz Co-authored-by: ccQpein Co-authored-by: Chad Brokaw Co-authored-by: Charles Saracco Co-authored-by: Chris Overcash Co-authored-by: Christian Pérez-Llamas <932644+chris-zen@users.noreply.github.com> Co-authored-by: Christopher Anderson Co-authored-by: Christopher N. Hesse Co-authored-by: Christopher Serr Co-authored-by: Christoph Herzog Co-authored-by: Christoph Co-authored-by: Christoph Co-authored-by: colinfruit <17092461+colinfruit@users.noreply.github.com> Co-authored-by: Colin Rofls Co-authored-by: Connor Brewster Co-authored-by: crumblingstatue Co-authored-by: Daniel Dulaney Co-authored-by: Daniel Kessler Co-authored-by: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Co-authored-by: Dan Knutson Co-authored-by: David Soria Parra Co-authored-by: David Soria Parra Co-authored-by: DetaNexus Co-authored-by: Diggory Hardy Co-authored-by: Dmitry Borodin <11879032+Dmitry-Borodin@users.noreply.github.com> Co-authored-by: Dmitry Borodin Co-authored-by: Dmitry Borodin Co-authored-by: Downtime <40173605+Cupnfish@users.noreply.github.com> Co-authored-by: Downtime <40173605+YRainbbbb@users.noreply.github.com> Co-authored-by: DrGabble Co-authored-by: DUDDINO RITARDATO Co-authored-by: Edwin Cheng Co-authored-by: Edwin Cheng Co-authored-by: Egor Larionov Co-authored-by: Emilio Gonzalez Co-authored-by: Emilio González Co-authored-by: Erik Gilling Co-authored-by: Felix Rabe Co-authored-by: ForLoveOfCats <30666851+ForLoveOfCats@users.noreply.github.com> Co-authored-by: ForLoveOfCats Co-authored-by: Gagandeep Singh <6944095+totsteps@users.noreply.github.com> Co-authored-by: Gagandeep Singh Co-authored-by: Garrett Risley <30666851+ForLoveOfCats@users.noreply.github.com> Co-authored-by: GitHub Co-authored-by: gmorenz Co-authored-by: Greg Morenz Co-authored-by: Gyusun Yeom Co-authored-by: hammsvietro <49427492+hammsvietro@users.noreply.github.com> Co-authored-by: Hampus Lidin Co-authored-by: Harry <36855805+minimal-state@users.noreply.github.com> Co-authored-by: Hilmar Gústafsson Co-authored-by: Hilmar Gústafsson Co-authored-by: HoNile Co-authored-by: HuszarG Co-authored-by: i509VCB Co-authored-by: Isaac Snow Co-authored-by: jaap aarts Co-authored-by: Jaap Aarts Co-authored-by: Jacob Zhong Co-authored-by: James Laver Co-authored-by: Jan Pochyla Co-authored-by: Jari Pennanen Co-authored-by: Jarrett Billingsley Co-authored-by: Jeff Muizelaar Co-authored-by: Jeff Parsons Co-authored-by: Jerboas86 <60667096+Jerboas86@users.noreply.github.com> Co-authored-by: Jerome Humbert Co-authored-by: jjl Co-authored-by: jneem Co-authored-by: Joe Neeman Co-authored-by: John Kåre Alsaker Co-authored-by: John Skottis Co-authored-by: John Terracina Co-authored-by: Jonas Platte Co-authored-by: Joshua Wade Co-authored-by: Joshua Wade Co-authored-by: joshuawade Co-authored-by: Justin Moon Co-authored-by: Kaiyin Zhong Co-authored-by: Kartoffelsaft <39356821+Kartoffelsaft@users.noreply.github.com> Co-authored-by: Kaur Kuut Co-authored-by: kbalt Co-authored-by: keenz Co-authored-by: Keith Simmons Co-authored-by: kud1ing Co-authored-by: Lassi Pulkkinen Co-authored-by: lazypassion <25536767+lazypassion@users.noreply.github.com> Co-authored-by: Lejero <37251497+Lejero@users.noreply.github.com> Co-authored-by: Leopold Luley Co-authored-by: Léo Stefanesco Co-authored-by: Lev Khoroshansky Co-authored-by: Lisael Co-authored-by: longmathemagician Co-authored-by: lord Co-authored-by: Maan2003 <49202620+Maan2003@users.noreply.github.com> Co-authored-by: Maan2003 Co-authored-by: Maan2003 Co-authored-by: Manmeet Maan <49202620+Maan2003@users.noreply.github.com> Co-authored-by: Manmeet Maan Co-authored-by: Manmeet Co-authored-by: Manmeet Singh Co-authored-by: Markus Kohlhase Co-authored-by: mastfissh Co-authored-by: Mathias Peters Co-authored-by: matt rice Co-authored-by: Maurizio Zucchelli Co-authored-by: Maximilian Köstler Co-authored-by: Melanie Riise Co-authored-by: Mendelt Siebenga Co-authored-by: MGlolenstine Co-authored-by: mobile-bungalow Co-authored-by: Moses Miller Co-authored-by: msiglreith Co-authored-by: Nico Burns Co-authored-by: Nils Martel Co-authored-by: Nils Martel Co-authored-by: Ofek Lev Co-authored-by: Olivier FAURE Co-authored-by: Olivier FAURE Co-authored-by: Paul Miller Co-authored-by: Paul Miller Co-authored-by: Pedro Hamms Co-authored-by: Per Lindgren Co-authored-by: Per Co-authored-by: Philipp Mildenberger Co-authored-by: Points Waves Co-authored-by: Psykopear Co-authored-by: pyroxymat <3037921+pyroxymat@users.noreply.github.com> Co-authored-by: pyroxymat Co-authored-by: Raph Levien Co-authored-by: Raph Levien Co-authored-by: raphlinus Co-authored-by: ratmice Co-authored-by: rhzk Co-authored-by: Richard Dodd (dodj) Co-authored-by: Richard Dodd Co-authored-by: Richard Poole Co-authored-by: rml Co-authored-by: Robert Lord Co-authored-by: Robert Wittams Co-authored-by: Roman Frołow Co-authored-by: Roman Zaynetdinov <627197+zaynetro@users.noreply.github.com> Co-authored-by: Roman Zaynetdinov Co-authored-by: Rose Hudson Co-authored-by: Ross Schulman Co-authored-by: Ruslan Kovtun Co-authored-by: Ruslan Prakapchuk Co-authored-by: Ryan Levick Co-authored-by: Ryan Levick Co-authored-by: sergej jurecko Co-authored-by: Shohei Ueda <30958501+peaceiris@users.noreply.github.com> Co-authored-by: sigaloid <69441971+sigaloid@users.noreply.github.com> Co-authored-by: sombrastudios Co-authored-by: Stanislav Lapata Co-authored-by: Starfighter Co-authored-by: Steven Malis Co-authored-by: Steven Co-authored-by: Steven Van Bael Co-authored-by: Sujit Joshi Co-authored-by: tay64 Co-authored-by: Ted de Munnik Co-authored-by: thecodewarrior Co-authored-by: Thierry Arnoux Co-authored-by: Thorbjørn Lindeijer Co-authored-by: Tim Schumacher Co-authored-by: tirix Co-authored-by: Tomasz Duda Co-authored-by: Tom Churchman Co-authored-by: totsteps <6944095+totsteps@users.noreply.github.com> Co-authored-by: totsteps Co-authored-by: Trent <2771466+tbillington@users.noreply.github.com> Co-authored-by: Uli Schlachter Co-authored-by: Uli Schlachter Co-authored-by: Valentin Kahl Co-authored-by: vishalsodani Co-authored-by: Vishal Sodani Co-authored-by: Walther Chen Co-authored-by: WeetHet <43210583+WeetHet@users.noreply.github.com> Co-authored-by: WeetHet Co-authored-by: Wicher Heldring Co-authored-by: xarvic Co-authored-by: xarvic Co-authored-by: Zarenor Darkstalker Co-authored-by: Zoxc Co-authored-by: Zoxc --- crates/masonry/.cargo/config.toml | 9 + crates/masonry/.github/workflows/ci.yml | 126 + crates/masonry/.gitignore | 35 + crates/masonry/ARCHITECTURE.md | 168 + crates/masonry/CONTRIBUTING.md | 30 + crates/masonry/Cargo.lock | 3186 +++++++++++++++++ crates/masonry/Cargo.toml | 53 + crates/masonry/LICENSE | 202 ++ crates/masonry/README.md | 157 + crates/masonry/ROADMAP.md | 334 ++ .../masonry/examples/assets/PicWithAlpha.png | Bin 0 -> 1784 bytes crates/masonry/examples/calc.rs | 362 ++ crates/masonry/examples/custom_widget.rs | 162 + crates/masonry/examples/hello.rs | 60 + crates/masonry/examples/readme.md | 7 + crates/masonry/examples/simple_image.rs | 45 + .../masonry/resources/i18n/de-DE/builtin.ftl | 41 + .../masonry/resources/i18n/en-US/builtin.ftl | 41 + .../masonry/resources/i18n/fr-CA/builtin.ftl | 41 + .../masonry/resources/i18n/it-IT/builtin.ftl | 41 + crates/masonry/src/action.rs | 50 + crates/masonry/src/app_driver.rs | 20 + crates/masonry/src/bloom.rs | 158 + crates/masonry/src/box_constraints.rs | 369 ++ crates/masonry/src/contexts.rs | 698 ++++ crates/masonry/src/debug_logger.rs | 266 ++ crates/masonry/src/debug_values.rs | 193 + crates/masonry/src/event.rs | 324 ++ crates/masonry/src/event_loop_runner.rs | 234 ++ crates/masonry/src/ext_event.rs | 105 + crates/masonry/src/lib.rs | 129 + crates/masonry/src/paint_scene_helpers.rs | 84 + crates/masonry/src/promise.rs | 162 + crates/masonry/src/render_root.rs | 507 +++ crates/masonry/src/testing/harness.rs | 557 +++ crates/masonry/src/testing/helper_widgets.rs | 347 ++ crates/masonry/src/testing/mod.rs | 49 + crates/masonry/src/testing/screenshots.rs | 44 + crates/masonry/src/testing/snapshot_utils.rs | 51 + crates/masonry/src/text_helpers.rs | 91 + crates/masonry/src/theme.rs | 86 + crates/masonry/src/util.rs | 86 + crates/masonry/src/widget/align.rs | 209 ++ crates/masonry/src/widget/button.rs | 248 ++ crates/masonry/src/widget/checkbox.rs | 261 ++ crates/masonry/src/widget/flex.rs | 1373 +++++++ crates/masonry/src/widget/image.rs | 187 + crates/masonry/src/widget/label.rs | 375 ++ crates/masonry/src/widget/mod.rs | 154 + crates/masonry/src/widget/portal.rs | 587 +++ ...asonry__widget__align__tests__centered.png | Bin 0 -> 5717 bytes .../masonry__widget__align__tests__left.png | Bin 0 -> 5708 bytes .../masonry__widget__align__tests__right.png | Bin 0 -> 5718 bytes .../masonry__widget__button__tests__hello.png | Bin 0 -> 6079 bytes ...widget__checkbox__tests__hello_checked.png | Bin 0 -> 6147 bytes ...dget__checkbox__tests__hello_unchecked.png | Bin 0 -> 5850 bytes ...__flex__tests__col_cross_axis_baseline.png | Bin 0 -> 8110 bytes ...et__flex__tests__col_cross_axis_center.png | Bin 0 -> 8110 bytes ...idget__flex__tests__col_cross_axis_end.png | Bin 0 -> 8076 bytes ...dget__flex__tests__col_cross_axis_fill.png | Bin 0 -> 8070 bytes ...get__flex__tests__col_cross_axis_start.png | Bin 0 -> 8070 bytes ...idget__flex__tests__col_fill_main_axis.png | Bin 0 -> 8172 bytes ...get__flex__tests__col_main_axis_center.png | Bin 0 -> 8105 bytes ...widget__flex__tests__col_main_axis_end.png | Bin 0 -> 8121 bytes ...flex__tests__col_main_axis_spaceAround.png | Bin 0 -> 8172 bytes ...lex__tests__col_main_axis_spaceBetween.png | Bin 0 -> 8198 bytes ...flex__tests__col_main_axis_spaceEvenly.png | Bin 0 -> 8185 bytes ...dget__flex__tests__col_main_axis_start.png | Bin 0 -> 8110 bytes ...__flex__tests__row_cross_axis_baseline.png | Bin 0 -> 7897 bytes ...et__flex__tests__row_cross_axis_center.png | Bin 0 -> 8007 bytes ...idget__flex__tests__row_cross_axis_end.png | Bin 0 -> 8006 bytes ...dget__flex__tests__row_cross_axis_fill.png | Bin 0 -> 7897 bytes ...get__flex__tests__row_cross_axis_start.png | Bin 0 -> 7897 bytes ...idget__flex__tests__row_fill_main_axis.png | Bin 0 -> 8018 bytes ...get__flex__tests__row_main_axis_center.png | Bin 0 -> 8017 bytes ...widget__flex__tests__row_main_axis_end.png | Bin 0 -> 8019 bytes ...flex__tests__row_main_axis_spaceAround.png | Bin 0 -> 8018 bytes ...lex__tests__row_main_axis_spaceBetween.png | Bin 0 -> 8049 bytes ...flex__tests__row_main_axis_spaceEvenly.png | Bin 0 -> 8013 bytes ...dget__flex__tests__row_main_axis_start.png | Bin 0 -> 8007 bytes ...onry__widget__image__tests__tall_paint.png | Bin 0 -> 1608 bytes .../masonry__widget__label__tests__hello.png | Bin 0 -> 5654 bytes ...widget__label__tests__line_break_modes.png | Bin 0 -> 20023 bytes ...ry__widget__label__tests__styled_label.png | Bin 0 -> 10535 bytes ...__portal__tests__button_list_no_scroll.png | Bin 0 -> 16401 bytes ...__tests__button_list_scroll_to_item_13.png | Bin 0 -> 17025 bytes ...l__tests__button_list_scroll_to_item_3.png | Bin 0 -> 16778 bytes ...t__portal__tests__button_list_scrolled.png | Bin 0 -> 16741 bytes ...t__scroll_bar__tests__scrollbar_bottom.png | Bin 0 -> 913 bytes ...__scroll_bar__tests__scrollbar_default.png | Bin 0 -> 962 bytes ...get__scroll_bar__tests__scrollbar_down.png | Bin 0 -> 909 bytes ...croll_bar__tests__scrollbar_horizontal.png | Bin 0 -> 721 bytes ...ar__tests__scrollbar_horizontal_middle.png | Bin 0 -> 697 bytes ...t__scroll_bar__tests__scrollbar_middle.png | Bin 0 -> 913 bytes ...y__widget__sized_box__tests__empty_box.png | Bin 0 -> 5247 bytes ...t__sized_box__tests__label_box_no_size.png | Bin 0 -> 6162 bytes ...__widget__spinner__tests__spinner_init.png | Bin 0 -> 25071 bytes ...masonry__widget__split__tests__columns.png | Bin 0 -> 6780 bytes .../masonry__widget__split__tests__rows.png | Bin 0 -> 6844 bytes ...masonry__widget__textbox__tests__hello.png | Bin 0 -> 5583 bytes ...y__widget__textbox__tests__placeholder.png | Bin 0 -> 6620 bytes crates/masonry/src/widget/scroll_bar.rs | 317 ++ crates/masonry/src/widget/sized_box.rs | 493 +++ ...sonry__widget__align__tests__centered.snap | 7 + .../masonry__widget__align__tests__left.snap | 7 + .../masonry__widget__align__tests__right.snap | 7 + ..._widget__button__tests__simple_button.snap | 9 + ...t__checkbox__tests__simple_checkbox-2.snap | 9 + ...get__checkbox__tests__simple_checkbox.snap | 9 + ...y__widget__label__tests__simple_label.snap | 6 + ...y__widget__portal__tests__button_list.snap | 52 + ...roll_bar__tests__horizontal_scrollbar.snap | 9 + ...__scroll_bar__tests__simple_scrollbar.snap | 9 + ...__widget__sized_box__tests__empty_box.snap | 7 + ...nry__widget__sized_box__tests__impl-2.snap | 9 + ...nry__widget__sized_box__tests__impl-3.snap | 9 + ...sonry__widget__sized_box__tests__impl.snap | 7 + ...__sized_box__tests__label_box_no_size.snap | 9 + ...sized_box__tests__label_box_with_size.snap | 9 + ...asonry__widget__split__tests__columns.snap | 8 + .../masonry__widget__split__tests__rows.snap | 8 + ...idget__textbox__tests__simple_textbox.snap | 13 + ...ox__tests__simple_textbox_placeholder.snap | 11 + crates/masonry/src/widget/spinner.rs | 191 + crates/masonry/src/widget/split.rs | 648 ++++ crates/masonry/src/widget/tests/layout.rs | 74 + .../src/widget/tests/lifecycle_basic.rs | 124 + .../src/widget/tests/lifecycle_disable.rs | 196 + .../src/widget/tests/lifecycle_focus.rs | 241 ++ crates/masonry/src/widget/tests/mod.rs | 12 + .../masonry/src/widget/tests/safety_rails.rs | 370 ++ ..._tests__lifecycle_basic__adding_child.snap | 21 + ..._tests__lifecycle_basic__app_creation.snap | 21 + ...idget__tests__lifecycle_basic__impl-2.snap | 22 + ..._widget__tests__lifecycle_basic__impl.snap | 38 + .../masonry/src/widget/tests/status_change.rs | 296 ++ crates/masonry/src/widget/widget.rs | 400 +++ crates/masonry/src/widget/widget_mut.rs | 87 + crates/masonry/src/widget/widget_pod.rs | 954 +++++ crates/masonry/src/widget/widget_ref.rs | 239 ++ crates/masonry/src/widget/widget_state.rs | 290 ++ 141 files changed, 18352 insertions(+) create mode 100644 crates/masonry/.cargo/config.toml create mode 100644 crates/masonry/.github/workflows/ci.yml create mode 100644 crates/masonry/.gitignore create mode 100644 crates/masonry/ARCHITECTURE.md create mode 100644 crates/masonry/CONTRIBUTING.md create mode 100644 crates/masonry/Cargo.lock create mode 100644 crates/masonry/Cargo.toml create mode 100644 crates/masonry/LICENSE create mode 100644 crates/masonry/README.md create mode 100644 crates/masonry/ROADMAP.md create mode 100644 crates/masonry/examples/assets/PicWithAlpha.png create mode 100644 crates/masonry/examples/calc.rs create mode 100644 crates/masonry/examples/custom_widget.rs create mode 100644 crates/masonry/examples/hello.rs create mode 100644 crates/masonry/examples/readme.md create mode 100644 crates/masonry/examples/simple_image.rs create mode 100644 crates/masonry/resources/i18n/de-DE/builtin.ftl create mode 100644 crates/masonry/resources/i18n/en-US/builtin.ftl create mode 100644 crates/masonry/resources/i18n/fr-CA/builtin.ftl create mode 100644 crates/masonry/resources/i18n/it-IT/builtin.ftl create mode 100644 crates/masonry/src/action.rs create mode 100644 crates/masonry/src/app_driver.rs create mode 100644 crates/masonry/src/bloom.rs create mode 100644 crates/masonry/src/box_constraints.rs create mode 100644 crates/masonry/src/contexts.rs create mode 100644 crates/masonry/src/debug_logger.rs create mode 100644 crates/masonry/src/debug_values.rs create mode 100644 crates/masonry/src/event.rs create mode 100644 crates/masonry/src/event_loop_runner.rs create mode 100644 crates/masonry/src/ext_event.rs create mode 100644 crates/masonry/src/lib.rs create mode 100644 crates/masonry/src/paint_scene_helpers.rs create mode 100644 crates/masonry/src/promise.rs create mode 100644 crates/masonry/src/render_root.rs create mode 100644 crates/masonry/src/testing/harness.rs create mode 100644 crates/masonry/src/testing/helper_widgets.rs create mode 100644 crates/masonry/src/testing/mod.rs create mode 100644 crates/masonry/src/testing/screenshots.rs create mode 100644 crates/masonry/src/testing/snapshot_utils.rs create mode 100644 crates/masonry/src/text_helpers.rs create mode 100644 crates/masonry/src/theme.rs create mode 100644 crates/masonry/src/util.rs create mode 100644 crates/masonry/src/widget/align.rs create mode 100644 crates/masonry/src/widget/button.rs create mode 100644 crates/masonry/src/widget/checkbox.rs create mode 100644 crates/masonry/src/widget/flex.rs create mode 100644 crates/masonry/src/widget/image.rs create mode 100644 crates/masonry/src/widget/label.rs create mode 100644 crates/masonry/src/widget/mod.rs create mode 100644 crates/masonry/src/widget/portal.rs create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__align__tests__centered.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__align__tests__left.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__align__tests__right.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__button__tests__hello.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__checkbox__tests__hello_checked.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__checkbox__tests__hello_unchecked.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__col_cross_axis_baseline.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__col_cross_axis_center.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__col_cross_axis_end.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__col_cross_axis_fill.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__col_cross_axis_start.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__col_fill_main_axis.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__col_main_axis_center.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__col_main_axis_end.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__col_main_axis_spaceAround.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__col_main_axis_spaceBetween.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__col_main_axis_spaceEvenly.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__col_main_axis_start.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__row_cross_axis_baseline.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__row_cross_axis_center.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__row_cross_axis_end.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__row_cross_axis_fill.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__row_cross_axis_start.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__row_fill_main_axis.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__row_main_axis_center.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__row_main_axis_end.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__row_main_axis_spaceAround.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__row_main_axis_spaceBetween.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__row_main_axis_spaceEvenly.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__flex__tests__row_main_axis_start.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__image__tests__tall_paint.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__label__tests__hello.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__label__tests__line_break_modes.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__label__tests__styled_label.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__portal__tests__button_list_no_scroll.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__portal__tests__button_list_scroll_to_item_13.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__portal__tests__button_list_scroll_to_item_3.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__portal__tests__button_list_scrolled.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__scroll_bar__tests__scrollbar_bottom.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__scroll_bar__tests__scrollbar_default.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__scroll_bar__tests__scrollbar_down.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__scroll_bar__tests__scrollbar_horizontal.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__scroll_bar__tests__scrollbar_horizontal_middle.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__scroll_bar__tests__scrollbar_middle.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__sized_box__tests__empty_box.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__sized_box__tests__label_box_no_size.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__spinner__tests__spinner_init.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__split__tests__columns.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__split__tests__rows.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__textbox__tests__hello.png create mode 100644 crates/masonry/src/widget/screenshots/masonry__widget__textbox__tests__placeholder.png create mode 100644 crates/masonry/src/widget/scroll_bar.rs create mode 100644 crates/masonry/src/widget/sized_box.rs create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__align__tests__centered.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__align__tests__left.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__align__tests__right.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__button__tests__simple_button.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__checkbox__tests__simple_checkbox-2.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__checkbox__tests__simple_checkbox.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__label__tests__simple_label.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__portal__tests__button_list.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__scroll_bar__tests__horizontal_scrollbar.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__scroll_bar__tests__simple_scrollbar.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__sized_box__tests__empty_box.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__sized_box__tests__impl-2.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__sized_box__tests__impl-3.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__sized_box__tests__impl.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__sized_box__tests__label_box_no_size.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__sized_box__tests__label_box_with_size.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__split__tests__columns.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__split__tests__rows.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__textbox__tests__simple_textbox.snap create mode 100644 crates/masonry/src/widget/snapshots/masonry__widget__textbox__tests__simple_textbox_placeholder.snap create mode 100644 crates/masonry/src/widget/spinner.rs create mode 100644 crates/masonry/src/widget/split.rs create mode 100644 crates/masonry/src/widget/tests/layout.rs create mode 100644 crates/masonry/src/widget/tests/lifecycle_basic.rs create mode 100644 crates/masonry/src/widget/tests/lifecycle_disable.rs create mode 100644 crates/masonry/src/widget/tests/lifecycle_focus.rs create mode 100644 crates/masonry/src/widget/tests/mod.rs create mode 100644 crates/masonry/src/widget/tests/safety_rails.rs create mode 100644 crates/masonry/src/widget/tests/snapshots/masonry__widget__tests__lifecycle_basic__adding_child.snap create mode 100644 crates/masonry/src/widget/tests/snapshots/masonry__widget__tests__lifecycle_basic__app_creation.snap create mode 100644 crates/masonry/src/widget/tests/snapshots/masonry__widget__tests__lifecycle_basic__impl-2.snap create mode 100644 crates/masonry/src/widget/tests/snapshots/masonry__widget__tests__lifecycle_basic__impl.snap create mode 100644 crates/masonry/src/widget/tests/status_change.rs create mode 100644 crates/masonry/src/widget/widget.rs create mode 100644 crates/masonry/src/widget/widget_mut.rs create mode 100644 crates/masonry/src/widget/widget_pod.rs create mode 100644 crates/masonry/src/widget/widget_ref.rs create mode 100644 crates/masonry/src/widget/widget_state.rs diff --git a/crates/masonry/.cargo/config.toml b/crates/masonry/.cargo/config.toml new file mode 100644 index 000000000..390e90d4a --- /dev/null +++ b/crates/masonry/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all())'] +# TODO - Replace with Cargo.toml config +rustflags = [ + # Global lints/warnings. + # We do this here instead of in the crate root because we want to apply + # these to example and test targets as well. + "-Aclippy::single_match", + "-Aclippy::bool_assert_comparison", +] diff --git a/crates/masonry/.github/workflows/ci.yml b/crates/masonry/.github/workflows/ci.yml new file mode 100644 index 000000000..711d24823 --- /dev/null +++ b/crates/masonry/.github/workflows/ci.yml @@ -0,0 +1,126 @@ +on: + push: + branches: + - master + pull_request: + +env: + SKIP_RENDER_SNAPSHOTS: 1 + +jobs: + rustfmt: + runs-on: ubuntu-latest + name: cargo fmt + steps: + - uses: actions/checkout@v2 + + - name: install stable toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + components: rustfmt + override: true + + - name: cargo fmt + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + test-stable: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macOS-latest, windows-2019, ubuntu-latest] + name: cargo clippy+test + steps: + - uses: actions/checkout@v2 + + - name: install libx11-dev + run: | + sudo apt update + sudo apt install libx11-dev libpango1.0-dev libxkbcommon-dev libxkbcommon-x11-dev + if: contains(matrix.os, 'ubuntu') + + - name: install stable toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: clippy + profile: minimal + override: true + + - name: restore cache + uses: Swatinem/rust-cache@v2 + + - name: cargo clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --all-targets --no-default-features + + # We use --all-targets to skip doc tests; we run them in a parallel task + - name: cargo test + uses: actions-rs/cargo@v1 + with: + command: test + args: --all-targets --no-default-features + + doctest-stable: + runs-on: macOS-latest + name: doctests + steps: + - uses: actions/checkout@v2 + + - name: install stable toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + + - name: restore cache + uses: Swatinem/rust-cache@v2 + + - name: cargo test --doc + uses: actions-rs/cargo@v1 + with: + command: test + args: --doc --no-default-features + + # This tests the future rust compiler to catch errors ahead of time without + # breaking CI + # We only run on a single OS to save time; this might let some errors go + # undetected until the compiler updates and they break CI; but that should + # happen rarely, and not pose too much of a problem when it does. + test-beta: + runs-on: macOS-latest + name: cargo clippy+check beta + steps: + - uses: actions/checkout@v2 + + - name: install beta toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: beta + components: clippy + profile: minimal + override: true + + - name: restore cache + uses: Swatinem/rust-cache@v2 + + - name: cargo check + uses: actions-rs/cargo@v1 + with: + command: check + args: --no-default-features + continue-on-error: true + + - name: cargo clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --all-targets --no-default-features + continue-on-error: true diff --git a/crates/masonry/.gitignore b/crates/masonry/.gitignore new file mode 100644 index 000000000..8ccf0ab4f --- /dev/null +++ b/crates/masonry/.gitignore @@ -0,0 +1,35 @@ +## Rust build files +target/ + +## Intellij Idea +.idea/ + +## Visual Studio Code +.vscode/ + +## Sublime Text +*.sublime-workspace +*.sublime-project + +## Vim +tags +Session.vim +Sessionx.vim + +## macOS +.DS_Store +.AppleDouble +.LSOverride + +## Windows +Thumbs.db +ehthumbs.db +ehthumbs_vista.db +*.lnk + +## Linux +*~ + +# Visual snapshots +**/screenshots/*.diff.png +**/screenshots/*.new.png## Rust diff --git a/crates/masonry/ARCHITECTURE.md b/crates/masonry/ARCHITECTURE.md new file mode 100644 index 000000000..7297b025f --- /dev/null +++ b/crates/masonry/ARCHITECTURE.md @@ -0,0 +1,168 @@ +# ARCHITECTURE + +Masonry is a framework that aims to provide the foundation for Rust GUI libraries. + +Developers trying to write immediate-mode GUIs, Elm-architecture GUIs, functional reactive GUIs, etc, can import Masonry and get a platform to create windows (using Glazier as a backend) each with a tree of widgets. Each widget has to implement the Widget trait that Masonry provides. + +This crate was originally a fork of Druid that emerged from discussions I had with Raph Levien and Colin Rofls about what it would look like to turn Druid into a foundational library. This means the code looks very similar to Druid's code and has mostly the same dependencies and primitives. + + +## High-level goals + +Masonry has some opinionated design goals: + +- **Be dumb.** As a general rule, Masonry doesn't do "algorithms". It has no reconciliation logic, no behind-the-scenes dataflow, no clever optimizations, etc. It tries to be efficient, but that efficiency comes from well-designed interfaces and well-placed abstraction boundaries. High-level logic should be implemented in downstream crates. +- **No mutability tricks.** Masonry tries to use as little unsafe code and cells/mutexes as possible. It's designed to work within Rust's ownership system, not to bypass it. +- **Facilitate testing.** Masonry implements a `TestHarness` type that helps users write unit tests, including tests with simulated user interactions and screenshot tests. In general, every feature should be designed with easy-to-write high-reliability tests in mind. +- **Facilitate debugging.** GUI app bugs are often easy to fix, but extremely painful to track down. GUI framework bugs are worse. Masonry should facilitate reproducing bugs and pinpointing which bit of code they come from. +- **Provide reflection.** Masonry should help developers surface some of the inherent structure in GUI programs. It should provide tools out-of-the-box to get information about the widget tree, performance indicators, etc. It should also provide accessibility data out-of-the-box. + + +## Code layout + +### `src/platform/` + +Some platform-specific stuff, for interfacing with Glazier. Relatively empty. + +### `src/testing/` + +Contains the TestHarness type, various helper widgets for writing tests, and the snapshot testing code. + +### `src/text/` + +Contains text-handling code, for both displaying and editing text. Hasn't been maintained in a while, here be dragons. + +### `src/widget/` + +Contains widget-related items, including the Widget trait, and the WidgetRef, WidgetMut, and WidgetPod types. + +Also includes a list of basic widgets, each defined in a single file. + +### `src/app_root.rs` + +The composition root of the framework. See **General architecture** section. + +### `src/debug_logger.rs`, `src/debug_values.rs` + +WIP logger to get record of widget passes. See issue #11. + +## Module organization principles + +(Some of these principles aren't actually applied in the codebase yet. See issue #14 on Github.) + +### Module structure + +Virtually every module should be private. The only public modules should be inline module blocks that gather public-facing re-exports. + +Most items should be exported from the root module, with no other public-facing export. This makes documentation more readable; readers don't need to click on multiple modules to find the item they're looking for. + +There should be only three public modules: + +- `widgets` +- `commands` +- `test_widgets` + +Module files should not be `foobar/mod.rs`. Instead, they should be `_foobar.rs` (thus in the parent folder); the full name is for readability, the leading underscore is so these names appear first in file hierarchies. + +### Imports + +We should avoid `use super::xxx` imports, except for specific cases like unit tests. + +Most imports in the code base should use the canonical top-level import. + +### No prelude + +Masonry should have no prelude. Examples and documentation should deliberately have a list of imports that cover everything users will need, so users can copy-paste these lists. + + +## General architecture + +### Platform handlers + +The composition root of Masonry is **AppRoot** and indirectly **AppRootInner** and **WindowRoot**. They are used by **MasonryWinHandler** and **MasonryAppHandler**. AppRoot, AppRootInner, and WindowRoot are in `src/app_root.rs`, MasonryWinHandler and MasonryAppHandler are in `src/platform/win_handler.rs`. + +In more detail: + +- **AppRootInner** is the real composition root. There is only a single one for any Masonry program. It calls WindowRoot methods in response to various events. +- **AppRoot** is the publicly exported root. It only owns a`Rc>` and its methods mostly just do locking and call AppRootInner methods. +- Each window open by Glazier owns a **MasonryWinHandler**, which implements `glazier::WinHandler`. Each MasonryWinHandler holds an AppRoot. +- The global application managed by Glazier owns a **MasonryAppHandler** which implements `glazier::AppHandler`. That MasonryAppHandler holds an AppRoot. These types are almost exclusively used in MacOS apps. +- AppRootInner owns a collection of **WindowRoot**. Each of them stores the Masonry data (widget tree, event data, other metadata) of a single window. + +To summarize, an application with N open windows will have: + +- N instances of MasonryWinHandler. +- 1 instance of MasonryAppHandler. +- N + 1 instances of AppRoot, which are shared references to... +- 1 instance of AppRootInner, which stores a vec of... +- N instances of WindowRoot. + +Additionally, each WindowRoot also stores a **WindowHandle**, which is a lightweight reference to the data structure created by Glazier to represent the window's platform-specific data (eg resource descriptors, rendering surface, etc). + +The high-level control flow of a Masonry app's render loop is usually: + +- The platform library (windows, x11, gtk, etc) runs some callbacks written in Glazier in response to user interactions, timers, or other events. +- The callbacks call MasonryWinHandler methods. +- Each method calls a single AppRoot method. +- That method calls some AppRootInner method. +- AppRootInner does a bunch of bookkeeping and calls WindowRoot methods. +- WindowRoot does a bunch of bookkeeping and calls the root WidgetPod's on_event/lifecycle/layout/paint methods. + +#### WindowConfig vs WindowDescription vs WindowBuilder + +WindowConfig, WindowDescription, and WindowBuilder have similar names and similar roles, so it might be a bit hard to tell them apart. A quick primer: + +- **WindowConfig** includes a bunch of window-specific metadata. Things like the window size, whether it's maximized, etc. That is used when creating the window, and also to update it. +- **WindowDescription** is WindowConfig plus some Masonry-specific data, such as a WidgetPod of the root widget. It's only used when creating a new window. +- **WindowBuilder** is a type exported by Glazier. When creating a new window, you first instantiate a WindowBuilder and give it config options as well as a type-erased instance of the MasonryWinHandler that will get events for that window. + + +### Widget hierarchy and passes + +The **Widget** trait is defined in `src/widget/widget.rs`. Most of the widget-related bookkeeping is done by the **WidgetPod** type defined in `src/widget/widget_pod.rs`. + +A WidgetPod is the main way to store a child for container widgets. In general, the widget hierarchy looks like a tree of container widgets, with each container owning a WidgetPod or a Vec of WidgetPod or something similar. When a pass (on_event/lifecycle/layout/paint) is run on a window, the matching method is called on the root WidgetPod, which recurses to its widget, which calls the same method on each of its children. + +Currently, container Widgets are encouraged to call pass methods on each of their children, even when the pass only concerns a single child (eg a click event where only one child is under the mouse); the filtering, if any, is done in WidgetPod. + +The current passes are: + +- **on_event:** Handles UX-related events, eg user interactions, timers, and IME updates. Widgets can declare these events as "handled" which has a bunch of semantic implications. +- **on_status_change:** TODO. +- **lifecycle:** Handles internal events, eg when the widget is marked as "disabled". +- **layout:** Given size constraints, return the widget's size. Container widgets first call their children's layout method, then set the position and layout date of each child. +- **paint** Paint the widget and its children. + +The general pass order is "For each user event, call on_event once, then lifecycle a variable number of times, then schedule a paint. When the platform starts the paint, run layout, then paint". + + +### WidgetMut + +In Masonry, widgets can't be mutated directly. All mutations go through a `WidgetMut` wrapper. So, to change a label's text, you might call `WidgetMut