Skip to content

Menu: Add icon property #8182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion api/cpp/cbindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ fn builtin_structs(path: &Path) -> anyhow::Result<()> {
writeln!(structs_priv, "// This file is auto-generated from {}", file!())?;
writeln!(structs_priv, "#include \"slint_builtin_structs.h\"")?;
writeln!(structs_priv, "#include \"slint_enums_internal.h\"")?;
writeln!(structs_priv, "#include \"slint_image.h\"")?;
writeln!(structs_priv, "namespace slint::cbindgen_private {{")?;
writeln!(structs_priv, "enum class KeyEventType : uint8_t;")?;
macro_rules! struct_file {
Expand Down Expand Up @@ -521,7 +522,7 @@ fn gen_corelib(
],
vec!["Color"],
"slint_image_internal.h",
"namespace slint::cbindgen_private { struct ParsedSVG{}; struct HTMLImage{}; using namespace vtable; namespace types{ struct NineSliceImage{}; } }",
"#include \"slint_color.h\"\nnamespace slint::cbindgen_private { struct ParsedSVG{}; struct HTMLImage{}; using namespace vtable; namespace types{ struct NineSliceImage{}; } }",
),
(
vec!["Color", "slint_color_brighter", "slint_color_darker",
Expand Down
16 changes: 0 additions & 16 deletions api/cpp/include/slint_color.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
#pragma once

#include "slint_color_internal.h"
#include "slint_properties.h"

#include <stdint.h>

namespace slint {

Expand Down Expand Up @@ -285,17 +282,4 @@ RgbaColor<float> Color::to_argb_float() const
return RgbaColor<float>(*this);
}

namespace private_api {

template<>
inline void
Property<Color>::set_animated_value(const Color &new_value,
const cbindgen_private::PropertyAnimation &animation_data) const
{
cbindgen_private::slint_property_set_animated_value_color(&inner, value, new_value,
&animation_data);
}

} // namespace private_api

} // namespace slint
9 changes: 9 additions & 0 deletions api/cpp/include/slint_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,15 @@ Property<float>::set_animated_value(const float &new_value,
&animation_data);
}

template<>
inline void
Property<Color>::set_animated_value(const Color &new_value,
const cbindgen_private::PropertyAnimation &animation_data) const
{
cbindgen_private::slint_property_set_animated_value_color(&inner, value, new_value,
&animation_data);
}

template<typename F>
void set_state_binding(const Property<StateInfo> &property, F binding)
{
Expand Down
10 changes: 10 additions & 0 deletions docs/astro/src/content/docs/reference/window/contextmenuarea.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ This is the label of the menu as written in the menu bar or in the parent menu.
When disabled, the `Menu` can be selected but not activated.
</SlintProperty>

### icon
<SlintProperty propName="icon" typeName="image">
The icon shown next to the title when in a parent menu.
</SlintProperty>

## `MenuItem`

A `MenuItem` represents a single menu entry. It's must be a child of a `Menu` element.
Expand All @@ -69,6 +74,11 @@ The title shown for this menu item.
When disabled, the `MenuItem` can be selected but not activated.
</SlintProperty>

### icon
<SlintProperty propName="icon" typeName="image">
The icon shown next to the title.
</SlintProperty>

### Callbacks of `MenuItem`

#### activated()
Expand Down
2 changes: 2 additions & 0 deletions examples/gallery/gallery.slint
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export component App inherits Window {
title: @tr("MenuBar" => "Save As");
for x in 10: MenuItem { title: @tr("MenuBar" => "Name {}", x+1); }
}
MenuSeparator {}
MenuItem { title: @tr("MenuBar" => "MenuItem with Icon"); icon: @image-url("thumbsup.png"); }
}
Menu {
title: @tr("MenuBar" => "Edit");
Expand Down
16 changes: 15 additions & 1 deletion internal/backends/winit/muda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,21 @@ impl MudaAdapter {
Box::new(muda::PredefinedMenuItem::separator())
} else if !entry.has_sub_menu && depth != 0 {
// the top level always has a sub menu regardless of entry.has_sub_menu
Box::new(muda::MenuItem::with_id(id.clone(), &entry.title, entry.enabled, None))
let icon = entry
.icon
.to_rgba8()
.map(|rgba| {
muda::Icon::from_rgba(rgba.as_bytes().to_vec(), rgba.width(), rgba.height())
.ok()
})
.flatten();
Box::new(muda::IconMenuItem::with_id(
id.clone(),
&entry.title,
entry.enabled,
icon,
None,
))
} else {
let sub_menu = muda::Submenu::with_id(id.clone(), &entry.title, entry.enabled);
if depth < 15 {
Expand Down
4 changes: 2 additions & 2 deletions internal/common/builtin_structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,8 @@ macro_rules! for_each_builtin_structs {
export {
/// The text of the menu entry
title: SharedString,
// /// the icon associated with the menu entry
// icon: Image,
/// the icon associated with the menu entry
icon: Image,
/// an opaque id that can be used to identify the menu entry
id: SharedString,
// keyboard_shortcut: KeySequence,
Expand Down
2 changes: 2 additions & 0 deletions internal/compiler/builtins.slint
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ component MenuItem {
in property <string> title;
callback activated();
in property <bool> enabled: true;
in property <image> icon;
//-disallow_global_types_as_child_elements
//-is_non_item_type
}
Expand All @@ -200,6 +201,7 @@ component MenuSeparator {
component Menu {
in property <string> title;
in property <bool> enabled: true;
in property <image> icon;
MenuItem {}
MenuSeparator {}
Menu {}
Expand Down
9 changes: 9 additions & 0 deletions internal/compiler/widgets/common/menu-base.slint
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ export component MenuItemBase {
padding-left: root.padding-left;
padding-right: root.padding-right;

spacing: 10px;

Image {
width: root.icon-size;
y: (parent.height - self.height) / 2;
source: entry.icon;
accessible-role: none;
}

label := Text {
text: entry.title;
color: root.default-foreground;
Expand Down
13 changes: 9 additions & 4 deletions internal/core/graphics/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This module contains image decoding and caching related types for the run-time l

use crate::lengths::{PhysicalPx, ScaleFactor};
use crate::slice::Slice;
#[allow(unused)]
use crate::{SharedString, SharedVector};

use super::{IntRect, IntSize};
Expand Down Expand Up @@ -287,21 +288,22 @@ pub struct StaticTextures {
/// time of the file it points to.
#[derive(PartialEq, Eq, Debug, Hash, Clone)]
#[repr(C)]
#[cfg(feature = "std")]
pub struct CachedPath {
path: SharedString,
/// SystemTime since UNIX_EPOC as secs
last_modified: u64,
last_modified: u32,
}

#[cfg(feature = "std")]
impl CachedPath {
#[cfg(feature = "std")]
fn new<P: AsRef<std::path::Path>>(path: P) -> Self {
let path_str = SharedString::from(path.as_ref().to_string_lossy().as_ref());
let path_str = path.as_ref().to_string_lossy().as_ref().into();
let timestamp = std::fs::metadata(path)
.and_then(|md| md.modified())
.unwrap_or(std::time::UNIX_EPOCH)
.duration_since(std::time::UNIX_EPOCH)
.map(|t| t.as_secs())
.map(|t| t.as_secs() as u32)
.unwrap_or_default();
Self { path: path_str, last_modified: timestamp }
}
Expand All @@ -315,6 +317,7 @@ pub enum ImageCacheKey {
/// This variant indicates that no image cache key can be created for the image.
/// For example this is the case for programmatically created images.
Invalid = 0,
#[cfg(feature = "std")]
/// The image is identified by its path on the file system and the last modification time stamp.
Path(CachedPath) = 1,
/// The image is identified by a URL.
Expand Down Expand Up @@ -1371,11 +1374,13 @@ pub(crate) mod ffi {
pub extern "C" fn slint_image_path(image: &Image) -> Option<&SharedString> {
match &image.0 {
ImageInner::EmbeddedImage { cache_key, .. } => match cache_key {
#[cfg(feature = "std")]
ImageCacheKey::Path(CachedPath { path, .. }) => Some(path),
_ => None,
},
ImageInner::NineSlice(nine) => match &nine.0 {
ImageInner::EmbeddedImage { cache_key, .. } => match cache_key {
#[cfg(feature = "std")]
ImageCacheKey::Path(CachedPath { path, .. }) => Some(path),
_ => None,
},
Expand Down
2 changes: 1 addition & 1 deletion internal/core/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ When adding an item or a property, it needs to be kept in sync with different pl
#![allow(non_upper_case_globals)]
#![allow(missing_docs)] // because documenting each property of items is redundant

use crate::graphics::{Brush, Color, FontRequest};
use crate::graphics::{Brush, Color, FontRequest, Image};
use crate::input::{
FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEventResult,
KeyEventType, MouseEvent,
Expand Down
5 changes: 4 additions & 1 deletion internal/core/menus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// for MenuVTable_static
#![allow(unsafe_code)]

use crate::graphics::Image;
use crate::item_rendering::CachedRenderingData;
use crate::item_tree::{ItemTreeRc, ItemWeak, VisitChildrenResult};
use crate::items::{ItemRc, ItemRef, MenuEntry, VoidArg};
Expand Down Expand Up @@ -86,11 +87,12 @@ impl MenuFromItemTree {
let children = self.update_shadow_tree_recursive(&item);
let has_sub_menu = !children.is_empty();
let enabled = menu_item.enabled();
let icon = menu_item.icon();
self.item_cache.borrow_mut().insert(
id.clone(),
ShadowTreeNode { item: ItemRc::downgrade(&item), children },
);
result.push(MenuEntry { title, id, has_sub_menu, is_separator, enabled });
result.push(MenuEntry { title, id, has_sub_menu, is_separator, enabled, icon });
}
VisitChildrenResult::CONTINUE
};
Expand Down Expand Up @@ -145,6 +147,7 @@ pub struct MenuItem {
pub title: Property<SharedString>,
pub activated: Callback<VoidArg>,
pub enabled: Property<bool>,
pub icon: Property<Image>,
}

impl crate::items::Item for MenuItem {
Expand Down
Loading