feat: responsive menu bar (#938)
This commit is contained in:
parent
b6c033562b
commit
eea916d783
6 changed files with 373 additions and 286 deletions
571
Cargo.lock
generated
571
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -73,7 +73,7 @@ uzers = "0.12.1"
|
||||||
git = "https://github.com/pop-os/libcosmic.git"
|
git = "https://github.com/pop-os/libcosmic.git"
|
||||||
default-features = false
|
default-features = false
|
||||||
#TODO: a11y feature crashes
|
#TODO: a11y feature crashes
|
||||||
features = ["multi-window", "tokio", "winit"]
|
features = ["multi-window", "tokio", "winit", "surface-message"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [
|
default = [
|
||||||
|
|
|
||||||
|
|
@ -4892,6 +4892,7 @@ impl Application for App {
|
||||||
|
|
||||||
fn header_start(&self) -> Vec<Element<Self::Message>> {
|
fn header_start(&self) -> Vec<Element<Self::Message>> {
|
||||||
vec![menu::menu_bar(
|
vec![menu::menu_bar(
|
||||||
|
&self.core,
|
||||||
self.tab_model.active_data::<Tab>(),
|
self.tab_model.active_data::<Tab>(),
|
||||||
&self.config,
|
&self.config,
|
||||||
&self.key_binds,
|
&self.key_binds,
|
||||||
|
|
|
||||||
59
src/menu.rs
59
src/menu.rs
|
|
@ -1,18 +1,19 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
|
app::Core,
|
||||||
iced::{Alignment, Background, Border, Length},
|
iced::{Alignment, Background, Border, Length},
|
||||||
theme,
|
theme,
|
||||||
widget::{
|
widget::{
|
||||||
self, button, column, container, divider, horizontal_space,
|
self, button, column, container, divider, horizontal_space,
|
||||||
menu::{self, key_bind::KeyBind, ItemHeight, ItemWidth, MenuBar},
|
menu::{self, key_bind::KeyBind, ItemHeight, ItemWidth, MenuBar},
|
||||||
text, Row,
|
responsive_menu_bar, text, Row,
|
||||||
},
|
},
|
||||||
Element,
|
Element,
|
||||||
};
|
};
|
||||||
use i18n_embed::LanguageLoader;
|
use i18n_embed::LanguageLoader;
|
||||||
use mime_guess::Mime;
|
use mime_guess::Mime;
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, sync::LazyLock};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{Action, Message},
|
app::{Action, Message},
|
||||||
|
|
@ -21,6 +22,9 @@ use crate::{
|
||||||
tab::{self, HeadingOptions, Location, LocationMenuAction, Tab},
|
tab::{self, HeadingOptions, Location, LocationMenuAction, Tab},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static MENU_ID: LazyLock<cosmic::widget::Id> =
|
||||||
|
LazyLock::new(|| cosmic::widget::Id::new("responsive-menu"));
|
||||||
|
|
||||||
macro_rules! menu_button {
|
macro_rules! menu_button {
|
||||||
($($x:expr),+ $(,)?) => (
|
($($x:expr),+ $(,)?) => (
|
||||||
button::custom(
|
button::custom(
|
||||||
|
|
@ -121,7 +125,7 @@ pub fn context_menu<'a>(
|
||||||
let lang_id = crate::localize::LANGUAGE_LOADER.current_language();
|
let lang_id = crate::localize::LANGUAGE_LOADER.current_language();
|
||||||
let language = lang_id.language.as_str();
|
let language = lang_id.language.as_str();
|
||||||
// Cache?
|
// Cache?
|
||||||
cosmic::desktop::load_desktop_file(Some(language), path)
|
cosmic::desktop::load_desktop_file(&[language.into()], path.into())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -485,6 +489,7 @@ pub fn dialog_menu(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn menu_bar<'a>(
|
pub fn menu_bar<'a>(
|
||||||
|
core: &Core,
|
||||||
tab_opt: Option<&Tab>,
|
tab_opt: Option<&Tab>,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
key_binds: &HashMap<KeyBind, Action>,
|
key_binds: &HashMap<KeyBind, Action>,
|
||||||
|
|
@ -519,11 +524,18 @@ pub fn menu_bar<'a>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MenuBar::new(vec![
|
responsive_menu_bar()
|
||||||
menu::Tree::with_children(
|
.item_height(ItemHeight::Dynamic(40))
|
||||||
menu::root(fl!("file")),
|
.item_width(ItemWidth::Uniform(360))
|
||||||
menu::items(
|
.spacing(theme::active().cosmic().spacing.space_xxxs.into())
|
||||||
key_binds,
|
.into_element(
|
||||||
|
core,
|
||||||
|
key_binds,
|
||||||
|
MENU_ID.clone(),
|
||||||
|
Message::Surface,
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
fl!("file"),
|
||||||
vec![
|
vec![
|
||||||
menu::Item::Button(fl!("new-tab"), None, Action::TabNew),
|
menu::Item::Button(fl!("new-tab"), None, Action::TabNew),
|
||||||
menu::Item::Button(fl!("new-window"), None, Action::WindowNew),
|
menu::Item::Button(fl!("new-window"), None, Action::WindowNew),
|
||||||
|
|
@ -554,11 +566,8 @@ pub fn menu_bar<'a>(
|
||||||
menu::Item::Button(fl!("quit"), None, Action::WindowClose),
|
menu::Item::Button(fl!("quit"), None, Action::WindowClose),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
(
|
||||||
menu::Tree::with_children(
|
(fl!("edit")),
|
||||||
menu::root(fl!("edit")),
|
|
||||||
menu::items(
|
|
||||||
key_binds,
|
|
||||||
vec![
|
vec![
|
||||||
menu_button_optional(fl!("cut"), Action::Cut, selected > 0),
|
menu_button_optional(fl!("cut"), Action::Cut, selected > 0),
|
||||||
menu_button_optional(fl!("copy"), Action::Copy, selected > 0),
|
menu_button_optional(fl!("copy"), Action::Copy, selected > 0),
|
||||||
|
|
@ -568,11 +577,8 @@ pub fn menu_bar<'a>(
|
||||||
menu::Item::Button(fl!("history"), None, Action::EditHistory),
|
menu::Item::Button(fl!("history"), None, Action::EditHistory),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
(
|
||||||
menu::Tree::with_children(
|
(fl!("view")),
|
||||||
menu::root(fl!("view")),
|
|
||||||
menu::items(
|
|
||||||
key_binds,
|
|
||||||
vec![
|
vec![
|
||||||
menu::Item::Button(fl!("zoom-in"), None, Action::ZoomIn),
|
menu::Item::Button(fl!("zoom-in"), None, Action::ZoomIn),
|
||||||
menu::Item::Button(fl!("default-size"), None, Action::ZoomDefault),
|
menu::Item::Button(fl!("default-size"), None, Action::ZoomDefault),
|
||||||
|
|
@ -621,11 +627,8 @@ pub fn menu_bar<'a>(
|
||||||
menu::Item::Button(fl!("menu-about"), None, Action::About),
|
menu::Item::Button(fl!("menu-about"), None, Action::About),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
(
|
||||||
menu::Tree::with_children(
|
(fl!("sort")),
|
||||||
menu::root(fl!("sort")),
|
|
||||||
menu::items(
|
|
||||||
key_binds,
|
|
||||||
vec![
|
vec![
|
||||||
sort_item(fl!("sort-a-z"), tab::HeadingOptions::Name, true),
|
sort_item(fl!("sort-a-z"), tab::HeadingOptions::Name, true),
|
||||||
sort_item(fl!("sort-z-a"), tab::HeadingOptions::Name, false),
|
sort_item(fl!("sort-z-a"), tab::HeadingOptions::Name, false),
|
||||||
|
|
@ -660,13 +663,9 @@ pub fn menu_bar<'a>(
|
||||||
//TODO: sort by type
|
//TODO: sort by type
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
])
|
)
|
||||||
.item_height(ItemHeight::Dynamic(40))
|
}
|
||||||
.item_width(ItemWidth::Uniform(360))
|
|
||||||
.spacing(theme::active().cosmic().spacing.space_xxxs.into())
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn location_context_menu<'a>(ancestor_index: usize) -> Element<'a, tab::Message> {
|
pub fn location_context_menu<'a>(ancestor_index: usize) -> Element<'a, tab::Message> {
|
||||||
//TODO: only add some of these when in App mode
|
//TODO: only add some of these when in App mode
|
||||||
|
|
|
||||||
|
|
@ -200,10 +200,10 @@ impl From<&desktop::DesktopEntryData> for MimeApp {
|
||||||
name: app.name.clone(),
|
name: app.name.clone(),
|
||||||
exec: app.exec.clone(),
|
exec: app.exec.clone(),
|
||||||
icon: match &app.icon {
|
icon: match &app.icon {
|
||||||
desktop::IconSource::Name(name) => {
|
desktop::fde::IconSource::Name(name) => {
|
||||||
widget::icon::from_name(name.as_str()).size(32).handle()
|
widget::icon::from_name(name.as_str()).size(32).handle()
|
||||||
}
|
}
|
||||||
desktop::IconSource::Path(path) => widget::icon::from_path(path.clone()),
|
desktop::fde::IconSource::Path(path) => widget::icon::from_path(path.clone()),
|
||||||
},
|
},
|
||||||
is_default: false,
|
is_default: false,
|
||||||
}
|
}
|
||||||
|
|
@ -251,24 +251,24 @@ impl MimeAppCache {
|
||||||
self.terminals.clear();
|
self.terminals.clear();
|
||||||
|
|
||||||
//TODO: get proper locale?
|
//TODO: get proper locale?
|
||||||
let locale = None;
|
let locale = &[];
|
||||||
|
|
||||||
// Load desktop applications by supported mime types
|
// Load desktop applications by supported mime types
|
||||||
//TODO: hashmap for all apps by id?
|
//TODO: hashmap for all apps by id?
|
||||||
let all_apps = desktop::load_applications(locale, false);
|
let mut all_apps = desktop::load_applications(locale, false);
|
||||||
for app in all_apps.iter() {
|
for app in &mut all_apps {
|
||||||
for mime in app.mime_types.iter() {
|
for mime in app.mime_types.iter() {
|
||||||
let apps = self
|
let apps = self
|
||||||
.cache
|
.cache
|
||||||
.entry(mime.clone())
|
.entry(mime.clone())
|
||||||
.or_insert_with(|| Vec::with_capacity(1));
|
.or_insert_with(|| Vec::with_capacity(1));
|
||||||
if !apps.iter().any(|x| x.id == app.id) {
|
if !apps.iter().any(|x| x.id == app.id) {
|
||||||
apps.push(MimeApp::from(app));
|
apps.push(MimeApp::from(&app));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for category in app.categories.iter() {
|
for category in app.categories.iter() {
|
||||||
if category == "TerminalEmulator" {
|
if category == "TerminalEmulator" {
|
||||||
self.terminals.push(MimeApp::from(app));
|
self.terminals.push(MimeApp::from(&app));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -335,9 +335,9 @@ impl MimeAppCache {
|
||||||
.or_insert_with(|| Vec::with_capacity(1));
|
.or_insert_with(|| Vec::with_capacity(1));
|
||||||
if !apps.iter().any(|x| filename_eq(&x.path, filename)) {
|
if !apps.iter().any(|x| filename_eq(&x.path, filename)) {
|
||||||
if let Some(app) =
|
if let Some(app) =
|
||||||
all_apps.iter().find(|x| filename_eq(&x.path, filename))
|
all_apps.find(|x| filename_eq(&x.path, filename))
|
||||||
{
|
{
|
||||||
apps.push(MimeApp::from(app));
|
apps.push(MimeApp::from(&app));
|
||||||
} else {
|
} else {
|
||||||
log::debug!("failed to add association for {:?}: application {:?} not found", mime, filename);
|
log::debug!("failed to add association for {:?}: application {:?} not found", mime, filename);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1537,7 +1537,7 @@ impl Item {
|
||||||
widget::image(handle.clone()).into()
|
widget::image(handle.clone()).into()
|
||||||
}
|
}
|
||||||
ItemThumbnail::Svg(handle) => widget::svg(handle.clone()).into(),
|
ItemThumbnail::Svg(handle) => widget::svg(handle.clone()).into(),
|
||||||
ItemThumbnail::Text(content) => widget::text_editor(content)
|
ItemThumbnail::Text(content) => widget::text_editor(&content)
|
||||||
.class(cosmic::theme::iced::TextEditor::Custom(Box::new(
|
.class(cosmic::theme::iced::TextEditor::Custom(Box::new(
|
||||||
text_editor_class,
|
text_editor_class,
|
||||||
)))
|
)))
|
||||||
|
|
@ -2721,10 +2721,10 @@ impl Tab {
|
||||||
items.iter().find(|item| item.selected).and_then(|item| {
|
items.iter().find(|item| item.selected).and_then(|item| {
|
||||||
let location = item.location_opt.as_ref()?;
|
let location = item.location_opt.as_ref()?;
|
||||||
let path = location.path_opt()?;
|
let path = location.path_opt()?;
|
||||||
cosmic::desktop::load_desktop_file(Some(language), path)
|
cosmic::desktop::load_desktop_file(&[language.into()], path.into())
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|path| cosmic::desktop::load_desktop_file(Some(language), path),
|
|path| cosmic::desktop::load_desktop_file(&[language.into()], path),
|
||||||
) {
|
) {
|
||||||
Some(entry) => commands.push(Command::ExecEntryAction(entry, action)),
|
Some(entry) => commands.push(Command::ExecEntryAction(entry, action)),
|
||||||
None => log::warn!("Invalid desktop entry path passed to ExecEntryAction"),
|
None => log::warn!("Invalid desktop entry path passed to ExecEntryAction"),
|
||||||
|
|
@ -3633,7 +3633,7 @@ impl Tab {
|
||||||
ItemThumbnail::Text(text) => {
|
ItemThumbnail::Text(text) => {
|
||||||
element_opt = Some(
|
element_opt = Some(
|
||||||
widget::container(
|
widget::container(
|
||||||
widget::text_editor(text).padding(space_xxs).class(
|
widget::text_editor(&text).padding(space_xxs).class(
|
||||||
cosmic::theme::iced::TextEditor::Custom(Box::new(
|
cosmic::theme::iced::TextEditor::Custom(Box::new(
|
||||||
text_editor_class,
|
text_editor_class,
|
||||||
)),
|
)),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue