#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
#![warn(unused_extern_crates)]
#![warn(missing_docs)]
zng_wgt::enable_widget_macros!();
use colors::BASE_COLOR_VAR;
use zng_ext_font::FontNames;
use zng_ext_input::{focus::FOCUS, mouse::ClickMode};
use zng_ext_l10n::lang;
use zng_wgt::{align, base_color, is_disabled, is_mobile, margin, prelude::*};
use zng_wgt_access::{access_role, AccessRole};
use zng_wgt_button::BUTTON;
use zng_wgt_container::{child_align, child_end, padding};
use zng_wgt_fill::{background_color, foreground_highlight};
use zng_wgt_filter::{opacity, saturate};
use zng_wgt_input::{click_mode, focus::is_focused, mouse::on_pre_mouse_enter};
use zng_wgt_input::{cursor, focus::alt_focus_scope, CursorIcon};
use zng_wgt_size_offset::size;
use zng_wgt_style::{impl_style_fn, style_fn, Style, StyleMix};
use zng_wgt_text::Text;
pub mod context;
pub mod popup;
pub mod sub;
#[widget($crate::Menu {
($children:expr) => {
children = $children;
}
})]
pub struct Menu(StyleMix<zng_wgt_panel::Panel>);
impl Menu {
fn widget_intrinsic(&mut self) {
self.style_intrinsic(STYLE_FN_VAR, property_id!(self::style_fn));
widget_set! {
self;
alt_focus_scope = true;
zng_wgt_panel::panel_fn = PANEL_FN_VAR;
style_base_fn = style_fn!(|_| DefaultStyle!());
access_role = AccessRole::Menu;
}
}
}
impl_style_fn!(Menu);
context_var! {
pub static PANEL_FN_VAR: WidgetFn<zng_wgt_panel::PanelArgs> = wgt_fn!(|a: zng_wgt_panel::PanelArgs| {
zng_wgt_wrap::Wrap! {
children = a.children;
}
});
pub static SHORTCUT_SPACING_VAR: Length = 20;
}
#[property(CONTEXT, default(PANEL_FN_VAR), widget_impl(Menu))]
pub fn panel_fn(child: impl UiNode, panel: impl IntoVar<WidgetFn<zng_wgt_panel::PanelArgs>>) -> impl UiNode {
with_context_var(child, PANEL_FN_VAR, panel)
}
#[widget($crate::DefaultStyle)]
pub struct DefaultStyle(Style);
impl DefaultStyle {
fn widget_intrinsic(&mut self) {
widget_set! {
self;
base_color = light_dark(rgb(0.82, 0.82, 0.82), rgb(0.18, 0.18, 0.18));
zng_wgt_button::style_fn = style_fn!(|_| ButtonStyle!());
zng_wgt_toggle::style_fn = style_fn!(|_| ToggleStyle!());
zng_wgt_rule_line::hr::color = BASE_COLOR_VAR.shade(1);
zng_wgt_text::icon::ico_size = 18;
}
}
}
#[widget($crate::ButtonStyle)]
pub struct ButtonStyle(Style);
impl ButtonStyle {
fn widget_intrinsic(&mut self) {
widget_set! {
self;
replace = true;
sub::column_width_padding = true;
padding = (4, 0);
child_align = Align::START;
base_color = light_dark(rgb(0.82, 0.82, 0.82), rgb(0.18, 0.18, 0.18));
background_color = BASE_COLOR_VAR.rgba();
opacity = 90.pct();
foreground_highlight = unset!;
zng_wgt_tooltip::tooltip_fn = WidgetFn::nil(); click_mode = ClickMode::release();access_role = AccessRole::MenuItem;
on_pre_mouse_enter = hn!(|_| {
FOCUS.focus_widget(WIDGET.id(), false);
});
shortcut_txt = Text! {
txt = BUTTON.cmd().flat_map(|c| match c {
Some(c) => c.shortcut_txt(),
None => LocalVar(Txt::from("")).boxed()
});
align = Align::CENTER;
};
icon_fn = BUTTON.cmd().flat_map(|c| match c {
Some(c) => c.icon().boxed(),
None => LocalVar(WidgetFn::nil()).boxed()
});
when *#is_focused {
background_color = BASE_COLOR_VAR.shade(1);
opacity = 100.pct();
}
when *#is_disabled {
saturate = false;
opacity = 50.pct();
cursor = CursorIcon::NotAllowed;
}
when *#is_mobile {
shortcut_txt = NilUiNode;
}
}
}
}
#[widget($crate::TouchButtonStyle)]
pub struct TouchButtonStyle(Style);
impl TouchButtonStyle {
fn widget_intrinsic(&mut self) {
widget_set! {
self;
zng_wgt::corner_radius = 0;
zng_wgt::visibility = BUTTON
.cmd()
.flat_map(|c| match c {
Some(c) => c.is_enabled().boxed(),
None => LocalVar(true).boxed(),
})
.map_into();
}
}
}
#[widget($crate::ToggleStyle)]
pub struct ToggleStyle(ButtonStyle);
impl ToggleStyle {
fn widget_intrinsic(&mut self) {
widget_set! {
self;
replace = true;
click_mode = ClickMode::release();
access_role = AccessRole::MenuItemCheckBox;
sub::start_column_fn = wgt_fn!(|_| Text! {
size = 1.2.em();
font_family = FontNames::system_ui(&lang!(und));
align = Align::CENTER;
txt = "✓";
when #{zng_wgt_toggle::IS_CHECKED_VAR}.is_none() {
txt = "━";
}
font_color = zng_wgt_text::FONT_COLOR_VAR.map(|c| c.transparent());
when #{zng_wgt_toggle::IS_CHECKED_VAR}.unwrap_or(true) {
font_color = zng_wgt_text::FONT_COLOR_VAR;
}
})
}
}
}
#[property(FILL)]
pub fn icon(child: impl UiNode, icon: impl UiNode) -> impl UiNode {
sub::start_column(child, icon)
}
#[property(FILL)]
pub fn icon_fn(child: impl UiNode, icon: impl IntoVar<WidgetFn<()>>) -> impl UiNode {
sub::start_column_fn(child, icon)
}
#[property(CHILD_CONTEXT)]
pub fn shortcut_txt(child: impl UiNode, shortcut: impl UiNode) -> impl UiNode {
let shortcut = margin(shortcut, sub::END_COLUMN_WIDTH_VAR.map(|w| SideOffsets::new(0, w.clone(), 0, 0)));
child_end(child, shortcut, SHORTCUT_SPACING_VAR)
}
#[property(CONTEXT, default(SHORTCUT_SPACING_VAR))]
pub fn shortcut_spacing(child: impl UiNode, spacing: impl IntoVar<Length>) -> impl UiNode {
with_context_var(child, SHORTCUT_SPACING_VAR, spacing)
}