mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00
Allow normal Element to be in the Dialog's button row with dialog-button-role
This commit is contained in:
parent
fcf55084ab
commit
ae3e1b4680
13 changed files with 165 additions and 65 deletions
|
@ -116,6 +116,7 @@ fn gen_corelib(root_dir: &Path, include_dir: &Path) -> anyhow::Result<()> {
|
|||
"ImageFit",
|
||||
"FillRule",
|
||||
"StandardButtonKind",
|
||||
"DialogButtonRole",
|
||||
"PointerEventKind",
|
||||
"PointerEventButton",
|
||||
"PointerEvent",
|
||||
|
|
|
@ -17,6 +17,7 @@ These properties are valid on all visible items
|
|||
children with transparency. 0 is fully transparent (invisible), and 1 is fully opaque. (default: 1)
|
||||
* **`visible`** (*bool*): When set to `false`, the element and all his children will not be drawn
|
||||
and not react to mouse input (default: `true`)
|
||||
* **`dialog-button-role`** (*enum DialogButtonRole*): Specify that this is a button in a `Dialog`.
|
||||
|
||||
|
||||
### Drop Shadows
|
||||
|
@ -656,13 +657,15 @@ Example := Window {
|
|||
Dialog is like a window, but it has buttons that are automatically laid out.
|
||||
|
||||
A Dialog should have one main element for the content, that is not a button.
|
||||
And the window can have any number of `StandardButton` widgets.
|
||||
And the window can have any number of `StandardButton` widgets or other button
|
||||
with the `dialog-button-role` property.
|
||||
The button will be layed out in an order that depends on the platform.
|
||||
|
||||
The `kind` property of the `StandardButton`s needs to be set to a specific value. It cannot be a complex expression,
|
||||
and there cannot be several button of the same kind.
|
||||
The `kind` property of the `StandardButton`s and the ``dialog-button-role` properties needs to be set to a specific value,
|
||||
it cannot be a complex expression.
|
||||
There cannot be several StandardButton of the same kind.
|
||||
|
||||
If A callback `<kind>_clicked` is automatically added for each button which does not have an explicit
|
||||
If A callback `<kind>_clicked` is automatically added for each StandardButton which does not have an explicit
|
||||
callback handler, so it can be handled from the native code. (e.g. if there is a button of kind `cancel`,
|
||||
a `cancel_clicked` callback will be added)
|
||||
When viewed with the `sixtyfps-viewer` program, the `ok`, `cancel`, and `close` button will cause the dialog to close.
|
||||
|
@ -675,13 +678,17 @@ When viewed with the `sixtyfps-viewer` program, the `ok`, `cancel`, and `close`
|
|||
### Example
|
||||
|
||||
```60
|
||||
import { StandardButton } from "sixtyfps_widgets.60";
|
||||
import { StandardButton, Button } from "sixtyfps_widgets.60";
|
||||
Example := Dialog {
|
||||
Text {
|
||||
text: "This is a dialog box";
|
||||
}
|
||||
StandardButton { kind: ok; }
|
||||
StandardButton { kind: cancel; }
|
||||
Button {
|
||||
text: "More Info";
|
||||
dialog-button-role: action;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -789,3 +796,19 @@ This enum describes the different ways of deciding what the inside of a shape de
|
|||
|
||||
* **`FillRule.nonzero`**: The ["nonzero" fill rule as defined in SVG](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule#nonzero).
|
||||
* **`FillRule.evenodd`**: The ["evenodd" fill rule as defined in SVG](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule#evenodd).
|
||||
|
||||
## `DialogButtonRole`
|
||||
|
||||
This enum represent the value of the `dialog-button-role` property which can be added to
|
||||
any element within a `Dialog` to put that item in the button row, and its exact position
|
||||
depends on the role and the platform.
|
||||
|
||||
### Values
|
||||
|
||||
* **`none`**: This is not a button means to go in the row of button of the dialog
|
||||
* **`accept`**: This is the role of the main button to click to accept the dialog. e.g. "Ok" or "Yes"
|
||||
* **`reject`**: This is the role of the main button to click to reject the dialog. e.g. "Cancel" or "No"
|
||||
* **`apply`**: This is the role of the "Apply" button
|
||||
* **`reset`**: This is the role of the "Reset" button
|
||||
* **`help`**: This is the role of the "Help" button
|
||||
* **`action`**: This is the role of any other button that perform another action.
|
|
@ -396,7 +396,7 @@ pub struct GridLayout {
|
|||
|
||||
/// When this GridLyout is actually the layout of a Dialog, then the cells start with all the buttons,
|
||||
/// and this variable contains their roles. The string is actually one of the values from the sixtyfps_corelib::layout::DialogButtonRole
|
||||
pub dialog_button_roles: Option<Vec<&'static str>>,
|
||||
pub dialog_button_roles: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl GridLayout {
|
||||
|
|
|
@ -405,19 +405,24 @@ fn lower_dialog_layout(
|
|||
let mut seen_buttons = HashSet::new();
|
||||
let layout_children = std::mem::take(&mut dialog_element.borrow_mut().children);
|
||||
for layout_child in &layout_children {
|
||||
let is_button = layout_child.borrow().property_declarations.get("kind").map_or(false, |pd| {
|
||||
let dialog_button_role_binding =
|
||||
layout_child.borrow_mut().bindings.remove("dialog-button-role");
|
||||
let is_button = if let Some(role_binding) = dialog_button_role_binding {
|
||||
if let Expression::EnumerationValue(val) = &role_binding.expression {
|
||||
let en = &val.enumeration;
|
||||
debug_assert_eq!(en.name, "DialogButtonRole");
|
||||
button_roles.push(en.values[val.value].clone());
|
||||
if val.value == 0 {
|
||||
diag.push_error("The `dialog-button-role` cannot be set explicitly to none".into(), &role_binding);
|
||||
}
|
||||
} else {
|
||||
diag.push_error("The `dialog-button-role` property must be known at compile-time".into(), &role_binding);
|
||||
}
|
||||
true
|
||||
} else if layout_child.borrow().property_declarations.get("kind").map_or(false, |pd| {
|
||||
matches!(&pd.property_type, Type::Enumeration(e) if e.name == "StandardButtonKind")
|
||||
});
|
||||
|
||||
if is_button {
|
||||
grid.add_element_with_coord(
|
||||
layout_child,
|
||||
(1, button_roles.len() as u16),
|
||||
(1, 1),
|
||||
&layout_cache_prop_h,
|
||||
&layout_cache_prop_v,
|
||||
diag,
|
||||
);
|
||||
}) {
|
||||
// layout_child is a StandardButton
|
||||
match layout_child.borrow().bindings.get("kind") {
|
||||
None => diag.push_error(
|
||||
"The `kind` property of the StandardButton in a Dialog must be set".into(),
|
||||
|
@ -429,20 +434,20 @@ fn lower_dialog_layout(
|
|||
debug_assert_eq!(en.name, "StandardButtonKind");
|
||||
let kind = &en.values[val.value];
|
||||
let role = match kind.as_str() {
|
||||
"ok" => "Accept",
|
||||
"cancel" => "Reject",
|
||||
"apply" => "Apply",
|
||||
"close" => "Reject",
|
||||
"reset" => "Reset",
|
||||
"help" => "Help",
|
||||
"yes" => "Accept",
|
||||
"no" => "Reject",
|
||||
"abort" => "Reject",
|
||||
"retry" => "Accept",
|
||||
"ignore" => "Accept",
|
||||
"ok" => "accept",
|
||||
"cancel" => "reject",
|
||||
"apply" => "apply",
|
||||
"close" => "reject",
|
||||
"reset" => "reset",
|
||||
"help" => "help",
|
||||
"yes" => "accept",
|
||||
"no" => "reject",
|
||||
"abort" => "reject",
|
||||
"retry" => "accept",
|
||||
"ignore" => "accept",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
button_roles.push(role);
|
||||
button_roles.push(role.into());
|
||||
if !seen_buttons.insert(val.value) {
|
||||
diag.push_error("Duplicated `kind`: There are two StandardButton in this Dialog with the same kind".into(), binding);
|
||||
} else if Rc::ptr_eq(
|
||||
|
@ -487,6 +492,20 @@ fn lower_dialog_layout(
|
|||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if is_button {
|
||||
grid.add_element_with_coord(
|
||||
layout_child,
|
||||
(1, button_roles.len() as u16),
|
||||
(1, 1),
|
||||
&layout_cache_prop_h,
|
||||
&layout_cache_prop_v,
|
||||
diag,
|
||||
);
|
||||
} else if main_widget.is_some() {
|
||||
diag.push_error(
|
||||
"A Dialog can have only one child element that is not a StandardButton".into(),
|
||||
|
@ -755,5 +774,8 @@ fn check_no_layout_properties(item: &ElementRc, diag: &mut BuildDiagnostics) {
|
|||
if matches!(prop.as_ref(), "col" | "row" | "colspan" | "rowspan") {
|
||||
diag.push_error(format!("{} used outside of a GridLayout", prop), expr);
|
||||
}
|
||||
if matches!(prop.as_ref(), "dialog-button-role") {
|
||||
diag.push_error(format!("{} used outside of a Dialog", prop), expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,12 +40,25 @@ MyDiag3 := Dialog {
|
|||
|
||||
}
|
||||
|
||||
MyDialog4 := Dialog {
|
||||
StandardButton { kind: ok; }
|
||||
Rectangle { dialog-button-role: accept; }
|
||||
Rectangle { dialog-button-role: none; }
|
||||
// ^error{The `dialog-button-role` cannot be set explicitly to none}
|
||||
Rectangle { dialog-button-role: true ? accept : reject; }
|
||||
// ^error{The `dialog-button-role` property must be known at compile-time}
|
||||
Rectangle {
|
||||
Rectangle { dialog-button-role: accept; }
|
||||
// ^error{dialog-button-role used outside of a Dialog}
|
||||
}
|
||||
}
|
||||
|
||||
Test := Rectangle {
|
||||
MyDiag1 {}
|
||||
MyDiag2 {} // FIXME: not the best place for the error
|
||||
// ^error{A Dialog must have a single child element that is not StandardButton}
|
||||
MyDiag3 {}
|
||||
MyDialog4 {}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -43,6 +43,24 @@ const RESERVED_LAYOUT_PROPERTIES: &[(&str, Type)] = &[
|
|||
("rowspan", Type::Int32),
|
||||
];
|
||||
|
||||
thread_local! {
|
||||
pub static DIALOG_BUTTON_ROLE_ENUM: Type =
|
||||
Type::Enumeration(Rc::new(Enumeration {
|
||||
name: "DialogButtonRole".into(),
|
||||
values: IntoIterator::into_iter([
|
||||
"none".to_owned(),
|
||||
"accept".to_owned(),
|
||||
"reject".to_owned(),
|
||||
"apply".to_owned(),
|
||||
"reset".to_owned(),
|
||||
"action".to_owned(),
|
||||
"help".to_owned(),
|
||||
])
|
||||
.collect(),
|
||||
default_value: 0,
|
||||
}));
|
||||
}
|
||||
|
||||
const RESERVED_OTHER_PROPERTIES: &[(&str, Type)] = &[
|
||||
("clip", Type::Bool),
|
||||
("opacity", Type::Float32),
|
||||
|
@ -67,6 +85,7 @@ pub fn reserved_properties() -> impl Iterator<Item = (&'static str, Type)> {
|
|||
.chain(std::array::IntoIter::new([
|
||||
("forward-focus", Type::ElementReference),
|
||||
("focus", BuiltinFunction::SetFocusItem.ty()),
|
||||
("dialog-button-role", DIALOG_BUTTON_ROLE_ENUM.with(|e| e.clone())),
|
||||
]))
|
||||
}
|
||||
|
||||
|
@ -183,6 +202,7 @@ impl TypeRegister {
|
|||
);
|
||||
declare_enum("PointerEventKind", &["cancel", "down", "up"]);
|
||||
declare_enum("PointerEventButton", &["none", "left", "right", "middle"]);
|
||||
register.insert_type(DIALOG_BUTTON_ROLE_ENUM.with(|x| x.clone()));
|
||||
|
||||
register.supported_property_animation_types.insert(Type::Float32.to_string());
|
||||
register.supported_property_animation_types.insert(Type::Int32.to_string());
|
||||
|
|
|
@ -1297,6 +1297,27 @@ impl Default for StandardButtonKind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Copy, Clone, Eq, PartialEq, Hash, Debug, strum_macros::EnumString, strum_macros::ToString,
|
||||
)]
|
||||
#[repr(C)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum DialogButtonRole {
|
||||
none,
|
||||
accept,
|
||||
reject,
|
||||
apply,
|
||||
reset,
|
||||
action,
|
||||
help,
|
||||
}
|
||||
|
||||
impl Default for DialogButtonRole {
|
||||
fn default() -> Self {
|
||||
Self::none
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, strum_macros::EnumString, strum_macros::Display)]
|
||||
#[repr(C)]
|
||||
#[allow(non_camel_case_types)]
|
||||
|
|
|
@ -11,7 +11,7 @@ LICENSE END */
|
|||
|
||||
// cspell:ignore coord
|
||||
|
||||
use crate::{slice::Slice, SharedVector};
|
||||
use crate::{items::DialogButtonRole, slice::Slice, SharedVector};
|
||||
|
||||
/// Vertical or Horizontal orientation
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
|
@ -719,18 +719,6 @@ pub fn solve_path_layout(data: &PathLayoutData, repeater_indexes: Slice<u32>) ->
|
|||
result
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Copy, Clone, Eq, PartialEq, Hash, Debug, strum_macros::EnumString, strum_macros::ToString,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum DialogButtonRole {
|
||||
Accept,
|
||||
Reject,
|
||||
Apply,
|
||||
Reset,
|
||||
Help,
|
||||
}
|
||||
|
||||
/// Given the cells of a layout of a Dialog, re-order the button according to the platform
|
||||
///
|
||||
/// This function assume that the `roles` contains the roles of the button which are the first `cells`
|
||||
|
@ -753,19 +741,21 @@ pub fn reorder_dialog_button_layout(cells: &mut [GridLayoutCellData], roles: &[D
|
|||
let mut idx = 0;
|
||||
|
||||
if cfg!(windows) {
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Reset);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::reset);
|
||||
idx += 1;
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Accept);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Reject);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Apply);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Help);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::accept);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::action);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::reject);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::apply);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::help);
|
||||
} else if cfg!(target_os = "macos") {
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Help);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Reset);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Apply);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::help);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::reset);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::apply);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::action);
|
||||
idx += 1;
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Reject);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Accept);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::reject);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::accept);
|
||||
|
||||
// assume some unix check if XDG_CURRENT_DESKTOP stats with K
|
||||
} else if std::env::var("XDG_CURRENT_DESKTOP")
|
||||
|
@ -774,20 +764,22 @@ pub fn reorder_dialog_button_layout(cells: &mut [GridLayoutCellData], roles: &[D
|
|||
.map_or(false, |x| x.to_ascii_uppercase() == b'K')
|
||||
{
|
||||
// KDE variant
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Help);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Reset);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::help);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::reset);
|
||||
idx += 1;
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Accept);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Apply);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Reject);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::action);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::accept);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::apply);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::reject);
|
||||
} else {
|
||||
// GNOME variant and fallback for WASM build
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Help);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Reset);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::help);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::reset);
|
||||
idx += 1;
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Apply);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Reject);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::Accept);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::action);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::apply);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::reject);
|
||||
add_buttons(cells, roles, &mut idx, DialogButtonRole::accept);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ declare_ValueType![
|
|||
crate::items::EventResult,
|
||||
crate::Brush,
|
||||
crate::items::FillRule,
|
||||
crate::items::DialogButtonRole,
|
||||
crate::items::StandardButtonKind,
|
||||
crate::graphics::Point,
|
||||
crate::items::PointerEvent,
|
||||
|
|
|
@ -315,6 +315,7 @@ declare_value_enum_conversion!(sixtyfps_corelib::items::FillRule, FillRule);
|
|||
declare_value_enum_conversion!(sixtyfps_corelib::items::StandardButtonKind, StandardButtonKind);
|
||||
declare_value_enum_conversion!(sixtyfps_corelib::items::PointerEventKind, PointerEventKind);
|
||||
declare_value_enum_conversion!(sixtyfps_corelib::items::PointerEventButton, PointerEventButton);
|
||||
declare_value_enum_conversion!(sixtyfps_corelib::items::DialogButtonRole, DialogButtonRole);
|
||||
|
||||
impl From<sixtyfps_corelib::animations::Instant> for Value {
|
||||
fn from(value: sixtyfps_corelib::animations::Instant) -> Self {
|
||||
|
|
|
@ -892,6 +892,7 @@ pub(crate) fn generate_component<'id>(
|
|||
"StandardButtonKind" => {
|
||||
property_info::<sixtyfps_corelib::items::StandardButtonKind>()
|
||||
}
|
||||
"DialogButtonRole" => property_info::<sixtyfps_corelib::items::DialogButtonRole>(),
|
||||
"PointerEventButton" => {
|
||||
property_info::<sixtyfps_corelib::items::PointerEventButton>()
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ use sixtyfps_compilerlib::langtype::Type;
|
|||
use sixtyfps_compilerlib::layout::{Layout, LayoutConstraints, LayoutGeometry, Orientation};
|
||||
use sixtyfps_compilerlib::namedreference::NamedReference;
|
||||
use sixtyfps_compilerlib::object_tree::ElementRc;
|
||||
use sixtyfps_corelib::items::DialogButtonRole;
|
||||
use sixtyfps_corelib::layout::{self as core_layout};
|
||||
use sixtyfps_corelib::model::RepeatedComponent;
|
||||
use sixtyfps_corelib::slice::Slice;
|
||||
|
@ -98,7 +99,7 @@ pub(crate) fn solve_layout(
|
|||
{
|
||||
let roles = buttons_roles
|
||||
.iter()
|
||||
.map(|r| core_layout::DialogButtonRole::from_str(r).unwrap())
|
||||
.map(|r| DialogButtonRole::from_str(r).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
core_layout::reorder_dialog_button_layout(&mut cells, &roles);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
Please contact info@sixtyfps.io for more information.
|
||||
LICENSE END */
|
||||
|
||||
import { StandardButton, GridBox } from "sixtyfps_widgets.60";
|
||||
import { StandardButton, Button, GridBox } from "sixtyfps_widgets.60";
|
||||
|
||||
TestCase := Dialog {
|
||||
Rectangle {
|
||||
|
@ -23,6 +23,10 @@ TestCase := Dialog {
|
|||
StandardButton { kind: apply; }
|
||||
StandardButton { kind: reset; }
|
||||
StandardButton { kind: yes; }
|
||||
Button {
|
||||
text: "Action";
|
||||
dialog-button-role: action;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue