T08b menus: menu bar over the native menus, searchable menu, pie menu; megamenu points to the navigation menu; menus follow their hosts
This commit is contained in:
18 files changed
+1516
-261
No files matched your search
@@ -16,8 +16,8 @@ use gpui::{
|
||||
|
||||
actions!(gallery, [Quit, About, LightMode, DarkMode, NewWindow]);
|
||||
|
||||
/// The gallery's menu bar and Dock menu: NativeMenu and JumpList at work.
|
||||
fn menus(window: WindowHandle<shell::Gallery>, cx: &mut App) {
|
||||
/// The gallery's menu bar and Dock menu: NativeMenu and JumpList at work. Set before the window opens, whose first frame may draw them.
|
||||
fn menus(cx: &mut App) {
|
||||
cx.set_menus(vec![
|
||||
Menu {
|
||||
name: "Ely".into(),
|
||||
@@ -36,6 +36,10 @@ fn menus(window: WindowHandle<shell::Gallery>, cx: &mut App) {
|
||||
},
|
||||
]);
|
||||
cx.set_dock_menu(vec![MenuItem::action("New Window", NewWindow)]);
|
||||
}
|
||||
|
||||
/// What the menus' actions do; the theme ones need the window.
|
||||
fn menu_actions(window: WindowHandle<shell::Gallery>, cx: &mut App) {
|
||||
cx.on_action(|_: &About, cx| pages::open_about(cx));
|
||||
cx.on_action(|_: &NewWindow, cx| pages::open_managed(cx));
|
||||
cx.on_action(move |_: &LightMode, cx| choose(window, shell::Choice::Light, cx));
|
||||
@@ -84,6 +88,7 @@ fn main() -> Result<()> {
|
||||
ely_gpui_component::init(cx);
|
||||
cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]);
|
||||
cx.on_action(|_: &Quit, cx| cx.quit());
|
||||
menus(cx);
|
||||
let bounds = Bounds::centered(None, size(px(1280.0), px(820.0)), cx);
|
||||
let options = WindowOptions {
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
@@ -100,7 +105,7 @@ fn main() -> Result<()> {
|
||||
cx.new(|cx| shell::Gallery::new(args.page.unwrap_or(0), window, cx))
|
||||
})
|
||||
.expect("gallery window failed to open");
|
||||
menus(window, cx);
|
||||
menu_actions(window, cx);
|
||||
cx.activate(true);
|
||||
if let Some(dir) = args.capture {
|
||||
capture::run(window, dir, args.page, cx);
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
mod more;
|
||||
mod rows;
|
||||
|
||||
use gpui::{AnyElement, App, IntoElement, ParentElement, Window, div};
|
||||
|
||||
use super::Page;
|
||||
use crate::script::Step;
|
||||
|
||||
pub const PAGE: Page = Page {
|
||||
number: 8,
|
||||
slug: "menus",
|
||||
title: "Menus",
|
||||
summary: "Rows of actions that open from a button, a right click or an arrow, and nest.",
|
||||
render,
|
||||
script: SCRIPT,
|
||||
};
|
||||
|
||||
const SCRIPT: &[Step] = &[
|
||||
Step::Rest,
|
||||
Step::Click("file-menu"),
|
||||
Step::Wait(300),
|
||||
Step::Key("down"),
|
||||
Step::Key("down"),
|
||||
Step::Key("down"),
|
||||
Step::Key("right"),
|
||||
Step::Wait(400),
|
||||
Step::Shot("submenu"),
|
||||
Step::Key("escape"),
|
||||
Step::Key("escape"),
|
||||
Step::Click("view-menu"),
|
||||
Step::Wait(400),
|
||||
Step::Shot("checks"),
|
||||
Step::Key("escape"),
|
||||
Step::ClickEnd("merge"),
|
||||
Step::Wait(400),
|
||||
Step::Shot("split"),
|
||||
Step::Key("escape"),
|
||||
Step::Click("more"),
|
||||
Step::Wait(400),
|
||||
Step::Shot("overflow"),
|
||||
Step::Key("escape"),
|
||||
Step::RightAt("canvas", 180.0, 70.0),
|
||||
Step::Wait(400),
|
||||
Step::Shot("context"),
|
||||
Step::Key("escape"),
|
||||
Step::DownAt("menubar", 16.0, 12.0),
|
||||
Step::UpAt("menubar", 16.0, 12.0),
|
||||
Step::Wait(400),
|
||||
Step::Shot("menubar"),
|
||||
Step::Key("right"),
|
||||
Step::Wait(300),
|
||||
Step::Shot("menubar-view"),
|
||||
Step::Key("escape"),
|
||||
Step::Click("move"),
|
||||
Step::Wait(400),
|
||||
Step::Type("re"),
|
||||
Step::Wait(300),
|
||||
Step::Shot("searchable"),
|
||||
Step::Key("escape"),
|
||||
Step::RightAt("pie-area", 210.0, 130.0),
|
||||
Step::Wait(600),
|
||||
Step::Key("right"),
|
||||
Step::Key("right"),
|
||||
Step::Wait(300),
|
||||
Step::Shot("pie"),
|
||||
Step::Key("escape"),
|
||||
Step::Rest,
|
||||
];
|
||||
|
||||
fn render(window: &mut Window, cx: &mut App) -> AnyElement {
|
||||
div()
|
||||
.child(rows::file_menu(window, cx))
|
||||
.child(rows::view_menu(window, cx))
|
||||
.child(rows::hosts(window, cx))
|
||||
.child(rows::context(window, cx))
|
||||
.child(more::menu_bar(cx))
|
||||
.child(more::mega_menu(cx))
|
||||
.child(more::searchable(window, cx))
|
||||
.child(more::pie(window, cx))
|
||||
.into_any_element()
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
use ely_gpui_component::{
|
||||
menus::{Menu, MenuBar, MenuItem, PieItem, PieMenu, SearchableMenu},
|
||||
primitives::IconName,
|
||||
theme::{ActiveTheme, Radius},
|
||||
typography::Caption,
|
||||
};
|
||||
use gpui::{App, IntoElement, ParentElement, Styled, Window, div, px};
|
||||
|
||||
use super::rows::{ran, run};
|
||||
use crate::{
|
||||
probe::probe,
|
||||
ui::{section, specimen},
|
||||
};
|
||||
|
||||
pub fn menu_bar(cx: &mut App) -> impl IntoElement + use<> {
|
||||
let menus = cx.get_menus().expect("the gallery sets its menus in main");
|
||||
section(
|
||||
"MenuBar Menu",
|
||||
"The app's own menus, the ones cx.set_menus gave the system, drawn in the window. Picking a row dispatches its action.",
|
||||
cx,
|
||||
)
|
||||
.child(specimen(
|
||||
"hover moves between menus while one is open",
|
||||
probe("menubar", MenuBar::new("app-bar", menus)),
|
||||
cx,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn mega_menu(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"MegaMenu",
|
||||
"A menu of links wide enough for a line of detail each.",
|
||||
cx,
|
||||
)
|
||||
.child(Caption::new(
|
||||
"It is navigation::NavigationMenu: each entry opens a panel of links. See Navigation, chapter 7.",
|
||||
))
|
||||
}
|
||||
|
||||
const PLACES: [(&str, IconName); 8] = [
|
||||
("Inbox", IconName::Inbox),
|
||||
("Roadmap", IconName::Map),
|
||||
("Design", IconName::PenTool),
|
||||
("Engineering", IconName::Code),
|
||||
("Research", IconName::BookOpen),
|
||||
("Marketing", IconName::Globe),
|
||||
("Archive", IconName::Archive),
|
||||
("Trash", IconName::Trash2),
|
||||
];
|
||||
|
||||
pub fn searchable(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let (last, caption) = ran("move-ran", window, cx);
|
||||
let menu = PLACES.into_iter().fold(Menu::new(), |menu, (name, icon)| {
|
||||
menu.item(MenuItem::new(name).icon(icon).on_click(run(&last, name)))
|
||||
});
|
||||
section(
|
||||
"SearchableMenu",
|
||||
"A filter field heads the menu. Typing narrows the rows, best first, and marks the letters that matched.",
|
||||
cx,
|
||||
)
|
||||
.child(specimen(
|
||||
caption,
|
||||
probe(
|
||||
"move",
|
||||
SearchableMenu::new("move", "Move to", menu)
|
||||
.icon(IconName::FolderOpen)
|
||||
.placeholder("Find a place"),
|
||||
),
|
||||
cx,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn pie(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let (last, caption) = ran("pie-ran", window, cx);
|
||||
let items = [
|
||||
(IconName::Copy, "Copy"),
|
||||
(IconName::Scissors, "Cut"),
|
||||
(IconName::Clipboard, "Paste"),
|
||||
(IconName::Share2, "Share"),
|
||||
(IconName::Pin, "Pin"),
|
||||
(IconName::Trash2, "Delete"),
|
||||
]
|
||||
.map(|(icon, name)| PieItem::new(icon, name).on_click(run(&last, name)));
|
||||
let theme = cx.theme();
|
||||
section(
|
||||
"PieMenu / RadialMenu",
|
||||
"A right click opens a ring at the pointer. The pointer's direction marks a slice and a click runs it; arrows turn the ring, the hub cancels.",
|
||||
cx,
|
||||
)
|
||||
.child(specimen(
|
||||
caption,
|
||||
probe(
|
||||
"pie-area",
|
||||
PieMenu::new("pie", items).child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.w(px(420.0))
|
||||
.h(px(260.0))
|
||||
.border_1()
|
||||
.border_dashed()
|
||||
.border_color(theme.colors.border_strong)
|
||||
.rounded(theme.radius(Radius::Lg))
|
||||
.text_color(theme.colors.fg_subtle)
|
||||
.child("Right-click for the ring"),
|
||||
),
|
||||
),
|
||||
cx,
|
||||
))
|
||||
}
|
||||
@@ -4,59 +4,15 @@ use ely_gpui_component::{
|
||||
primitives::IconName,
|
||||
theme::{ActiveTheme, Radius},
|
||||
};
|
||||
use gpui::{
|
||||
AnyElement, App, Entity, IntoElement, ParentElement, SharedString, Styled, Window, div, px,
|
||||
};
|
||||
use gpui::{App, Entity, IntoElement, ParentElement, SharedString, Styled, Window, div, px};
|
||||
|
||||
use super::Page;
|
||||
use crate::{
|
||||
probe::probe,
|
||||
script::Step,
|
||||
ui::{keep, row, section, set, specimen},
|
||||
};
|
||||
|
||||
pub const PAGE: Page = Page {
|
||||
number: 8,
|
||||
slug: "menus",
|
||||
title: "Menus",
|
||||
summary: "Rows of actions that open from a button, a right click or an arrow, and nest.",
|
||||
render,
|
||||
script: SCRIPT,
|
||||
};
|
||||
|
||||
const SCRIPT: &[Step] = &[
|
||||
Step::Rest,
|
||||
Step::Click("file-menu"),
|
||||
Step::Wait(300),
|
||||
Step::Key("down"),
|
||||
Step::Key("down"),
|
||||
Step::Key("down"),
|
||||
Step::Key("right"),
|
||||
Step::Wait(400),
|
||||
Step::Shot("submenu"),
|
||||
Step::Key("escape"),
|
||||
Step::Key("escape"),
|
||||
Step::Click("view-menu"),
|
||||
Step::Wait(400),
|
||||
Step::Shot("checks"),
|
||||
Step::Key("escape"),
|
||||
Step::ClickEnd("merge"),
|
||||
Step::Wait(400),
|
||||
Step::Shot("split"),
|
||||
Step::Key("escape"),
|
||||
Step::Click("more"),
|
||||
Step::Wait(400),
|
||||
Step::Shot("overflow"),
|
||||
Step::Key("escape"),
|
||||
Step::RightAt("canvas", 180.0, 70.0),
|
||||
Step::Wait(400),
|
||||
Step::Shot("context"),
|
||||
Step::Key("escape"),
|
||||
Step::Rest,
|
||||
];
|
||||
|
||||
/// Records the last row run, for the captions.
|
||||
fn ran(
|
||||
pub(super) fn ran(
|
||||
key: &'static str,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
@@ -69,7 +25,7 @@ fn ran(
|
||||
(last, caption)
|
||||
}
|
||||
|
||||
fn run(
|
||||
pub(super) fn run(
|
||||
last: &Entity<Option<SharedString>>,
|
||||
name: &'static str,
|
||||
) -> impl Fn(&mut Window, &mut App) + 'static {
|
||||
@@ -77,7 +33,7 @@ fn run(
|
||||
move |_, cx| set(&last, Some(name.into()), cx)
|
||||
}
|
||||
|
||||
fn file_menu(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
pub fn file_menu(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let (last, caption) = ran("file-ran", window, cx);
|
||||
let recent = Menu::new()
|
||||
.item(
|
||||
@@ -142,7 +98,7 @@ fn file_menu(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
))
|
||||
}
|
||||
|
||||
fn view_menu(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
pub fn view_menu(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let shown = keep("view-shown", || [true, false, true], window, cx);
|
||||
let density = keep("view-density", || 1usize, window, cx);
|
||||
let (now, dense) = (*shown.read(cx), *density.read(cx));
|
||||
@@ -192,7 +148,7 @@ fn view_menu(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
))
|
||||
}
|
||||
|
||||
fn hosts(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
pub fn hosts(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let (last, caption) = ran("hosts-ran", window, cx);
|
||||
let merge = Menu::new()
|
||||
.item(MenuItem::new("Squash and merge").on_click(run(&last, "squash and merge")))
|
||||
@@ -252,7 +208,7 @@ fn hosts(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
))
|
||||
}
|
||||
|
||||
fn context(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
pub fn context(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let (last, caption) = ran("context-ran", window, cx);
|
||||
let menu = Menu::new()
|
||||
.item(
|
||||
@@ -311,12 +267,3 @@ fn context(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
cx,
|
||||
))
|
||||
}
|
||||
|
||||
fn render(window: &mut Window, cx: &mut App) -> AnyElement {
|
||||
div()
|
||||
.child(file_menu(window, cx))
|
||||
.child(view_menu(window, cx))
|
||||
.child(hosts(window, cx))
|
||||
.child(context(window, cx))
|
||||
.into_any_element()
|
||||
}
|
||||
Reference in new issue
Block a user