Skip to content
Draft
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
9 changes: 4 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,10 @@ understory_event_state = { git = "https://github.com/jrmoulton/understory", rev
understory_index = { git = "https://github.com/jrmoulton/understory", rev = "2c2abb8c4bd9", package = "understory_index" }
understory_focus = { git = "https://github.com/jrmoulton/understory", rev = "2c2abb8c4bd9", package = "understory_focus" }
understory_virtual_list = { git = "https://github.com/jrmoulton/understory", rev = "2c2abb8c4bd9", package = "understory_virtual_list" }

[target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies]
muda = { workspace = true }
muda = { workspace = true, optional = true }

[target.'cfg(any(target_os = "linux"))'.dependencies]
muda = { workspace = true, default-features = false, features = ["gtk"] }
muda = { workspace = true, default-features = false, features = ["gtk"], optional = true }

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen-futures = { version = "0.4" }
Expand All @@ -145,7 +143,7 @@ objc2-app-kit = { version = "0.3", features = [
dispatch2 = {version = "0.3.0"}

[features]
default = ["editor", "default-image-formats", "vger", "serde"]
default = ["editor", "default-image-formats", "vger", "serde", "menus"]
skia = ["dep:floem_skia_renderer"]
vello = ["dep:floem_vello_renderer"]
vger = ["dep:floem_vger_renderer"]
Expand Down Expand Up @@ -182,6 +180,7 @@ image-qoi = ["image/qoi"]
image-tga = ["image/tga"]
image-tiff = ["image/tiff"]
image-webp = ["image/webp"]
menus = ["dep:muda"]
tokio = ["dep:tokio"]
# rfd (file dialog) async runtime
crossbeam = ["dep:crossbeam", "floem_renderer/crossbeam"]
Expand Down
12 changes: 7 additions & 5 deletions src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ use peniko::kurbo::{Point, Size, Vec2};
use winit::window::WindowId;
use winit::window::{ResizeDirection, Theme};

use crate::IntoView;
use crate::platform::{Duration, Instant};
use crate::IntoView;

#[cfg(feature = "menus")]
use crate::platform::menu::Menu;
use crate::{
app::{AppUpdateEvent, add_app_update_event},
message::{UPDATE_MESSAGES, UpdateMessage},
platform::menu::Menu,
app::{add_app_update_event, AppUpdateEvent},
message::{UpdateMessage, UPDATE_MESSAGES},
view::View,
view::ViewId,
views::Decorators,
Expand Down Expand Up @@ -260,6 +261,7 @@ where
/// - Windows: Yes
/// - macOS: Yes
/// - Linux: Uses a custom Floem View
#[cfg(feature = "menus")]
pub fn show_context_menu(menu: Menu, pos: Option<Point>) {
add_update_message(UpdateMessage::ShowContextMenu { menu, pos });
}
Expand All @@ -271,7 +273,7 @@ pub fn show_context_menu(menu: Menu, pos: Option<Point>) {
/// - macOS: Yes
/// - Linux: No
/// - wasm32: No
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(feature = "menus", not(target_arch = "wasm32")))]
pub fn set_window_menu(menu: Menu) {
add_update_message(UpdateMessage::WindowMenu { menu });
}
Expand Down
19 changes: 13 additions & 6 deletions src/app/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use crate::{
window::{WindowConfig, handle::WindowHandle, id::process_window_updates},
};

#[cfg(feature = "menus")]
struct PendingContextMenu {
window_id: WindowId,
menu: super::MenuWrapper,
Expand All @@ -46,6 +47,7 @@ pub(crate) struct ApplicationHandle {
window_handles: HashMap<winit::window::WindowId, WindowHandle>,
timers: HashMap<TimerToken, Timer>,
animating_windows: std::collections::HashSet<winit::window::WindowId>,
#[cfg(feature = "menus")]
pending_context_menus: Vec<PendingContextMenu>,
pub(crate) event_listener: Option<Box<AppEventCallback>>,
pub(crate) gpu_resources: Option<GpuResources>,
Expand All @@ -60,6 +62,7 @@ impl ApplicationHandle {
window_handles: HashMap::new(),
timers: HashMap::new(),
animating_windows: std::collections::HashSet::new(),
#[cfg(feature = "menus")]
pending_context_menus: Vec::new(),
event_listener: None,
gpu_resources: None,
Expand Down Expand Up @@ -114,6 +117,7 @@ impl ApplicationHandle {
panic!("Sent a gpu resource update after it had already been initialized");
}
}
#[cfg(feature = "menus")]
UserEvent::ShowContextMenu {
window_id,
menu,
Expand Down Expand Up @@ -197,7 +201,7 @@ impl ApplicationHandle {
}
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(feature = "menus", not(target_arch = "wasm32")))]
AppUpdateEvent::MenuAction { action_id } => {
for (_, handle) in self.window_handles.iter_mut() {
if handle.window_state.context_menu.contains_key(&action_id)
Expand All @@ -208,7 +212,7 @@ impl ApplicationHandle {
}
}
}
#[cfg(target_arch = "wasm32")]
#[cfg(all(feature = "menus", target_arch = "wasm32"))]
AppUpdateEvent::MenuAction { action_id } => {
for (_, handle) in self.window_handles.iter_mut() {
if handle.window_state.context_menu.contains_key(&action_id) {
Expand Down Expand Up @@ -824,10 +828,13 @@ impl ApplicationHandle {
}

pub(crate) fn flush_deferred_context_menus(&mut self) {
let pending = std::mem::take(&mut self.pending_context_menus);
for item in pending {
if let Some(handle) = self.window_handles.get_mut(&item.window_id) {
handle.show_context_menu(item.menu.0, item.pos);
#[cfg(feature = "menus")]
{
let pending = std::mem::take(&mut self.pending_context_menus);
for item in pending {
if let Some(handle) = self.window_handles.get_mut(&item.window_id) {
handle.show_context_menu(item.menu.0, item.pos);
}
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ use std::{
sync::atomic::{AtomicBool, Ordering},
};

#[cfg(feature = "menus")]
use crate::platform::menu_types::MenuId;
#[cfg(feature = "crossbeam")]
use crossbeam::channel::{Receiver, Sender, unbounded as channel};
#[cfg(feature = "menus")]
use peniko::kurbo::Point;
#[cfg(not(feature = "crossbeam"))]
use std::sync::mpsc::{Receiver, Sender, channel};
Expand Down Expand Up @@ -107,13 +109,16 @@ pub enum AppEvent {
Reopen { has_visible_windows: bool },
}

#[cfg(feature = "menus")]
pub(crate) struct MenuWrapper(pub(crate) muda::Menu);
// SAFETY: these unsafe wappers are needed so that we can send the muda memu.
// The muda menu internally uses RC on a String ID and it's Vec of children.
// This unsafe wrapper is memory safe but the race condition could potentially (unlikely)
// lead to bad reference counts and leaked memory.
// I think this is fine for this case.
#[cfg(feature = "menus")]
unsafe impl Send for MenuWrapper {}
#[cfg(feature = "menus")]
unsafe impl Sync for MenuWrapper {}

pub(crate) enum UserEvent {
Expand All @@ -128,6 +133,7 @@ pub(crate) enum UserEvent {
GpuResourcesUpdate {
window_id: WindowId,
},
#[cfg(feature = "menus")]
ShowContextMenu {
window_id: WindowId,
menu: MenuWrapper,
Expand Down Expand Up @@ -164,6 +170,7 @@ pub(crate) enum AppUpdateEvent {
CancelTimer {
timer: TimerToken,
},
#[cfg(feature = "menus")]
MenuAction {
action_id: MenuId,
},
Expand Down Expand Up @@ -282,7 +289,7 @@ impl Application {
}
let handle = ApplicationHandle::new(config);

#[cfg(any(target_os = "windows", target_os = "macos"))]
#[cfg(all(feature = "menus", any(target_os = "windows", target_os = "macos")))]
muda::MenuEvent::set_event_handler(Some(move |event: muda::MenuEvent| {
add_app_update_event(AppUpdateEvent::MenuAction {
action_id: event.id,
Expand Down
7 changes: 5 additions & 2 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ use peniko::kurbo::{Affine, Point, Rect};
use smallvec::SmallVec;
use std::{cell::RefCell, rc::Rc};

#[cfg(feature = "menus")]
use crate::platform::menu::Menu;
use crate::{
ElementId, custom_event,
custom_event,
event::{EventPropagation, Phase},
platform::menu::Menu,
style::recalc::StyleReason,
view::ViewId,
ElementId,
};

pub type EventCallback = dyn FnMut(&mut EventCx) -> EventPropagation;
pub type ResizeCallback = dyn Fn(Rect);
#[cfg(feature = "menus")]
pub type MenuCallback = dyn Fn() -> Menu;

bitflags::bitflags! {
Expand Down
21 changes: 13 additions & 8 deletions src/event/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ use understory_focus::{
};
use winit::keyboard::KeyCode;

#[cfg(feature = "menus")]
use crate::action::show_context_menu;
use crate::{
BoxTree, ElementId, ElementMeta, ViewId,
action::show_context_menu,
context::Phases,
event::{
DragEvent, DragToken, Event, FocusEvent, InteractionEvent, Phase, PointerCaptureEvent,
Expand Down Expand Up @@ -1195,13 +1196,16 @@ impl RouteCx<'_, '_> {
}

// Context / popout menus (platform-specific timing).
let pbe = match &self.event {
Event::Pointer(PointerEvent::Down(pbe)) if cfg!(target_os = "macos") => Some(pbe),
Event::Pointer(PointerEvent::Up(pbe)) if !cfg!(target_os = "macos") => Some(pbe),
_ => None,
};
if let Some(pbe) = pbe {
self.handle_menu_events(&pbe.clone());
#[cfg(feature = "menus")]
{
let pbe = match &self.event {
Event::Pointer(PointerEvent::Down(pbe)) if cfg!(target_os = "macos") => Some(pbe),
Event::Pointer(PointerEvent::Up(pbe)) if !cfg!(target_os = "macos") => Some(pbe),
_ => None,
};
if let Some(pbe) = pbe {
self.handle_menu_events(&pbe.clone());
}
}

// Window close — close the window if not prevented.
Expand Down Expand Up @@ -1243,6 +1247,7 @@ impl RouteCx<'_, '_> {
}
}

#[cfg(feature = "menus")]
fn handle_menu_events(&mut self, pbe: &PointerButtonEvent) {
let Some(button) = pbe.button else { return };
let Some(hit) = self
Expand Down
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ pub use layout::responsive;
/// Re-export file module from platform for backward compatibility.
pub use platform::file;
/// Re-export menu module from platform for backward compatibility.
#[cfg(feature = "menus")]
pub use platform::menu;
/// Re-export view_tuple module from view for backward compatibility.
pub use view::tuple as view_tuple;
Expand Down Expand Up @@ -230,7 +231,7 @@ pub use floem_renderer::Svg as RendererSvg;
pub use floem_renderer::gpu_resources::GpuResources;
pub use imbl;
pub use layout::ScreenLayout;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(feature = "menus", not(target_arch = "wasm32")))]
pub use muda;
pub use peniko;
pub use peniko::kurbo;
Expand All @@ -239,6 +240,7 @@ pub use platform::open_file;
#[cfg(not(target_arch = "wasm32"))]
pub use platform::save_as;
pub use platform::{Clipboard, ClipboardError, FileDialogOptions, FileInfo, FileSpec};
#[cfg(feature = "menus")]
pub use platform::{Menu, SubMenu};
pub use taffy;
pub use ui_events;
Expand Down
10 changes: 6 additions & 4 deletions src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ use peniko::kurbo::{Point, Rect, Size, Vec2};
use ui_events::pointer::PointerId;
use winit::window::{ResizeDirection, Theme};

#[cfg(feature = "menus")]
use crate::platform::menu::Menu;
use crate::{
ElementId,
event::{Event, RouteKind, listener},
platform::menu::Menu,
event::{listener, Event, RouteKind},
style::recalc::StyleReason,
view::{AnyView, View, ViewId},
window::state::WindowState,
ElementId,
};

thread_local! {
Expand Down Expand Up @@ -84,11 +85,12 @@ pub enum UpdateMessage {
DragWindow,
DragResizeWindow(ResizeDirection),
SetWindowDelta(Vec2),
#[cfg(feature = "menus")]
ShowContextMenu {
menu: Menu,
pos: Option<Point>,
},
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(feature = "menus", not(target_arch = "wasm32")))]
WindowMenu {
menu: Menu,
},
Expand Down
6 changes: 3 additions & 3 deletions src/platform/context_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
//! native context menus aren't fully supported by the muda crate.

use floem_reactive::{Effect, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith};
use peniko::Color;
use peniko::color::palette;
use peniko::kurbo::{Point, Size};
use peniko::Color;
use ui_events::keyboard::{Key, NamedKey};

use crate::context::VisualChangedListener;
Expand All @@ -17,7 +17,7 @@ use crate::style::CursorStyle;
use crate::platform::menu_types;
use crate::unit::UnitExt;
use crate::view::{IntoView, View};
use crate::views::{Container, Decorators, Label, Stack, svg};
use crate::views::{svg, Container, Decorators, Label, Stack};

#[derive(Clone, PartialEq, Eq, Hash)]
enum MenuDisplay {
Expand Down Expand Up @@ -105,7 +105,7 @@ pub(crate) fn context_menu_view(
window_size: RwSignal<Size>,
) -> impl IntoView {
use crate::{
app::{AppUpdateEvent, add_app_update_event},
app::{add_app_update_event, AppUpdateEvent},
views::dyn_stack,
};

Expand Down
4 changes: 2 additions & 2 deletions src/platform/menu_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

#[cfg(not(target_arch = "wasm32"))]
pub use muda::{
CheckMenuItem, Icon, IconMenuItem, IsMenuItem, Menu, MenuId, MenuItem, NativeIcon,
PredefinedMenuItem, Submenu, accelerator::Accelerator,
accelerator::Accelerator, CheckMenuItem, Icon, IconMenuItem, IsMenuItem, Menu, MenuId,
MenuItem, NativeIcon, PredefinedMenuItem, Submenu,
};

// MenuItemKind is only used in context_menu.rs, which is only compiled on Linux/FreeBSD/wasm32
Expand Down
8 changes: 7 additions & 1 deletion src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
//! features like clipboard access, native menus, and file dialogs.

pub(crate) mod clipboard;
#[cfg(any(target_os = "linux", target_os = "freebsd", target_arch = "wasm32"))]
#[cfg(all(
feature = "menus",
any(target_os = "linux", target_os = "freebsd", target_arch = "wasm32")
))]
pub(crate) mod context_menu;
pub mod file;
#[cfg(not(target_arch = "wasm32"))]
pub mod file_action;
#[cfg(feature = "menus")]
pub mod menu;
#[cfg(feature = "menus")]
pub(crate) mod menu_types;
pub(crate) mod time;
#[cfg(target_arch = "wasm32")]
Expand All @@ -19,5 +24,6 @@ pub use clipboard::{Clipboard, ClipboardError};
pub use file::{FileDialogOptions, FileInfo, FileSpec};
#[cfg(not(target_arch = "wasm32"))]
pub use file_action::{open_file, save_as};
#[cfg(feature = "menus")]
pub use menu::{Menu, SubMenu};
pub(crate) use time::{Duration, Instant};
Loading