compiler internal docs: expend the menu lowering docs
Some checks are pending
CI / docs (push) Blocked by required conditions
CI / wasm (push) Blocked by required conditions
CI / wasm_demo (push) Blocked by required conditions
CI / fmt_test (push) Blocked by required conditions
CI / esp-idf-quick (push) Blocked by required conditions
CI / android (push) Blocked by required conditions
CI / miri (push) Blocked by required conditions
CI / test-figma-inspector (push) Blocked by required conditions
autofix.ci / lint_typecheck (push) Waiting to run
autofix.ci / format_fix (push) Waiting to run
CI / python_test (windows-2022) (push) Blocked by required conditions
CI / tree-sitter (push) Blocked by required conditions
CI / updater_test (0.3.0) (push) Blocked by required conditions
CI / files-changed (push) Waiting to run
CI / python_test (macos-14) (push) Blocked by required conditions
CI / python_test (ubuntu-22.04) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, macos-14, stable) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, 1.85) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, beta) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, stable) (push) Blocked by required conditions
CI / build_and_test (ubuntu-22.04, 1.85) (push) Blocked by required conditions
CI / build_and_test (ubuntu-22.04, nightly) (push) Blocked by required conditions
CI / node_test (macos-14) (push) Blocked by required conditions
CI / node_test (ubuntu-22.04) (push) Blocked by required conditions
CI / node_test (windows-2022) (push) Blocked by required conditions
CI / cpp_test_driver (macos-13) (push) Blocked by required conditions
CI / cpp_test_driver (ubuntu-22.04) (push) Blocked by required conditions
CI / cpp_test_driver (windows-2022) (push) Blocked by required conditions
CI / cpp_cmake (macos-14, 1.85) (push) Blocked by required conditions
CI / cpp_cmake (ubuntu-22.04, stable) (push) Blocked by required conditions
CI / cpp_cmake (windows-2022, nightly) (push) Blocked by required conditions
CI / cpp_package_test (push) Blocked by required conditions
CI / vsce_build_test (push) Blocked by required conditions
CI / mcu (pico-st7789, thumbv6m-none-eabi) (push) Blocked by required conditions
CI / mcu (pico2-st7789, thumbv8m.main-none-eabihf) (push) Blocked by required conditions
CI / mcu (stm32h735g, thumbv7em-none-eabihf) (push) Blocked by required conditions
CI / mcu-embassy (push) Blocked by required conditions
CI / ffi_32bit_build (push) Blocked by required conditions

This commit is contained in:
Olivier Goffart 2025-06-29 16:20:22 +02:00
parent a316d741d5
commit 73dc1c7cdc

View file

@ -3,12 +3,16 @@
//! Passe lower the `MenuBar` and `ContextMenuArea` as well as all their contents
//!
//! We can't have properties of type model because that is not binary compatible with C++,
//! We can't have properties of type Model because that is not binary compatible with C++,
//! so all the code that handle model of MenuEntry need to be handle by code in the generated code
//! and transformed into a `SharedVector<MenuEntry>` that is passed to Slint runtime.
//!
//! ## MenuBar
//!
//! There are two kind of transformations:
//! In the simple case with no `if` or `for` inside the Menu, we directly lower to a MenuBarImpl
//! with `activated` and `sub-menu` callback completely generated by this pass
//!
//! ```slint
//! Window {
//! menu-bar := MenuBar {
@ -16,7 +20,7 @@
//! title: "File";
//! MenuItem {
//! title: "A";
//! activated => { ... }
//! activated => { debug("A") }
//! }
//! Menu {
//! title: "B";
@ -36,7 +40,7 @@
//! if(entry.id == "0") { return [ { id: "1", title: "A" }, { id: "2", title: "B", has-sub-menu: true } ]; }
//! else if(entry.id == "2") { return [ { id: "3", title: "C" } ]; } else { return []; }
//! }
//! callback activated() => { if (entry.id == "2") { ... } }
//! callback activated() => { if (entry.id == "2") { debug("A") } }
//! if !Builtin.supports_native_menu_bar() : MenuBarImpl {
//! entries: parent.entries
//! sub-menu(..) => { parent.sub-menu(..) }
@ -53,6 +57,51 @@
//! }
//! ```
//!
//! If the menu contains `if` or `for`, then we can't lower immediately to something at compile
//! time in this pass, and we generate an item tree for the menu
//! ```slint
//! Window {
//! menu-bar := MenuBar {
//! Menu {
//! title: "File";
//! if cond1 : MenuItem {
//! title: "A";
//! activated => { debug("A") }
//! }
//! Menu {
//! title: "B";
//! for x in 42 : MenuItem { title: "C" + x; }
//! }
//! }
//! }
//! content := ...
//! }
//! ```
//! Is transformed to
//! ```slint
//! Window {
//! menu-bar := VerticalLayout {
//! // these callbacks are connected by the setup_native_menu_bar call to an adapter from the menu tree
//! callback sub-menu(entry: MenuEntry);
//! callback activated();
//! if !Builtin.supports_native_menu_bar() : MenuBarImpl {
//! entries: parent.entries
//! sub-menu(..) => { parent.sub-menu(..) }
//! activated(..) => { parent.activated(..) }
//! }
//! Empty {
//! content := ...
//! }
//! }
//! init => {
//! // ... rest of init ...
//! // that function will always be called even for non-native.
//! // the menu-index is the index of the `Menu` element moved in the `object_tree::Component::menu_item_trees`
//! Builtin.setup_native_menu_bar(menu-bar.entries, menu-bar.sub-menu, menu-bar.activated, menu-index, no_native_menu)
//! }
//! }
//! ```
//!
//! ## ContextMenuInternal
//!
//! ```slint
@ -72,13 +121,34 @@
//! activated => { ... }
//!
//! // show is actually a callback called by the native code when right clicking
//! callback show(point) => { Builtin.show_context_menu(entries, sub-menu, activated, point) }
//! show(point) => { Builtin.show_popup_menu(self, self.entries, &self.sub-menu, &self.activated, point) }
//! }
//! ```
//!
//! ## ContextMenuArea
//!
//! This is the same as ContextMenuInternal, but entries, sub-menu, and activated are generated
//! from the MenuItem similar to MenuBar
//!
//! Similar to the MenuBar, there is a simple case for when the Menu don't have `if` or `for`,
//! in which the sub-menu and activated are just generated by this pass and this is same generated
//! code as for ContextMenuInternal
//!
//! For the complex case, we get a extra item tree in [`Component::menu_item_trees`]
//! and the call to `show_popup_menu` will be responsible to set the callback handler to the
//! `ContextMenu` item callbacks.
//!
//! ```slint
//! // A `ContextMenuArea` with a complex Menu with `if` and `for` will be lowered to:
//! menu := ContextMenu {
//! show(point) => {
//! // menu-index is an index in `Component::menu_item_trees`
//! // that function will set the handler to self.sub-menu and self.activated
//! Builtin.show_popup_menu(self, menu-index, &self.sub-menu, &self.activated, point)
//! }
//! }
//! ```
//!
use crate::diagnostics::{BuildDiagnostics, Spanned};
use crate::expression_tree::{BuiltinFunction, Callable, Expression, NamedReference};