Minor Tooling and UI Improvements (#1314)

* [wip]fix: disable fill and gradient tools on bmp images
- aims to fix #1161

* [wip]fix: disable gradient tool for bmp images

* fix: disable gradient tool for bitmap images
- fixes https://github.com/GraphiteEditor/Graphite/issues/1161

* [wip]fix: disable menu bar elements if no open document

* [WIP]feat: render certain menubar elements if document is open

* fix: conditional menu bar rendering fixes
- update menu bar on certain document actions
- fix menu bar order on rendering all elements

* refactor: change conditional menu elements property
- use existing 'disabled' property

* feat: render menu dropdown items conditionally

* fix: revert formatter whitespace changes

* refactor: improve variable naming and message usage
- name menu bar bool flag better
- use SendLayout message and remove unneeded calls
This commit is contained in:
Dhruv 2023-06-24 01:49:00 +05:30 committed by Keavon Chambers
parent 173398ad55
commit 9c2520111d
7 changed files with 137 additions and 78 deletions

View File

@ -37,12 +37,14 @@ pub struct MenuBarEntry {
pub shortcut: Option<ActionKeys>, pub shortcut: Option<ActionKeys>,
pub action: WidgetHolder, pub action: WidgetHolder,
pub children: MenuBarEntryChildren, pub children: MenuBarEntryChildren,
pub disabled: bool,
} }
impl MenuBarEntry { impl MenuBarEntry {
pub fn new_root(label: String, children: MenuBarEntryChildren) -> Self { pub fn new_root(label: String, disabled: bool, children: MenuBarEntryChildren) -> Self {
Self { Self {
label, label,
disabled,
children, children,
..Default::default() ..Default::default()
} }
@ -67,6 +69,7 @@ impl Default for MenuBarEntry {
shortcut: None, shortcut: None,
action: MenuBarEntry::no_action(), action: MenuBarEntry::no_action(),
children: MenuBarEntryChildren::empty(), children: MenuBarEntryChildren::empty(),
disabled: false,
} }
} }
} }

View File

@ -6,12 +6,15 @@ use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::prelude::*; use crate::messages::prelude::*;
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct MenuBarMessageHandler {} pub struct MenuBarMessageHandler {
no_active_document: bool,
}
impl MessageHandler<MenuBarMessage, ()> for MenuBarMessageHandler { impl MessageHandler<MenuBarMessage, bool> for MenuBarMessageHandler {
#[remain::check] #[remain::check]
fn process_message(&mut self, message: MenuBarMessage, responses: &mut VecDeque<Message>, _data: ()) { fn process_message(&mut self, message: MenuBarMessage, responses: &mut VecDeque<Message>, has_active_document: bool) {
use MenuBarMessage::*; use MenuBarMessage::*;
self.no_active_document = !has_active_document;
#[remain::sorted] #[remain::sorted]
match message { match message {
@ -26,75 +29,13 @@ impl MessageHandler<MenuBarMessage, ()> for MenuBarMessageHandler {
impl PropertyHolder for MenuBarMessageHandler { impl PropertyHolder for MenuBarMessageHandler {
fn properties(&self) -> Layout { fn properties(&self) -> Layout {
Layout::MenuLayout(MenuLayout::new(vec![ let no_active_document = self.no_active_document;
MenuBarEntry {
icon: Some("GraphiteLogo".into()), // enable these only if there's an active document
action: MenuBarEntry::create_action(|_| FrontendMessage::TriggerVisitLink { url: "https://graphite.rs".into() }.into()), let conditional_menu_entries = vec![
..Default::default()
},
MenuBarEntry::new_root(
"File".into(),
MenuBarEntryChildren(vec![
vec![
MenuBarEntry {
label: "New…".into(),
icon: Some("File".into()),
action: MenuBarEntry::create_action(|_| DialogMessage::RequestNewDocumentDialog.into()),
shortcut: action_keys!(DialogMessageDiscriminant::RequestNewDocumentDialog),
children: MenuBarEntryChildren::empty(),
},
MenuBarEntry {
label: "Open…".into(),
shortcut: action_keys!(PortfolioMessageDiscriminant::OpenDocument),
action: MenuBarEntry::create_action(|_| PortfolioMessage::OpenDocument.into()),
..MenuBarEntry::default()
},
],
vec![
MenuBarEntry {
label: "Close".into(),
shortcut: action_keys!(PortfolioMessageDiscriminant::CloseActiveDocumentWithConfirmation),
action: MenuBarEntry::create_action(|_| PortfolioMessage::CloseActiveDocumentWithConfirmation.into()),
..MenuBarEntry::default()
},
MenuBarEntry {
label: "Close All".into(),
shortcut: action_keys!(DialogMessageDiscriminant::CloseAllDocumentsWithConfirmation),
action: MenuBarEntry::create_action(|_| DialogMessage::CloseAllDocumentsWithConfirmation.into()),
..MenuBarEntry::default()
},
],
vec![MenuBarEntry {
label: "Save".into(),
shortcut: action_keys!(DocumentMessageDiscriminant::SaveDocument),
action: MenuBarEntry::create_action(|_| DocumentMessage::SaveDocument.into()),
..MenuBarEntry::default()
}],
vec![
MenuBarEntry {
label: "Import…".into(),
shortcut: action_keys!(PortfolioMessageDiscriminant::Import),
action: MenuBarEntry::create_action(|_| PortfolioMessage::Import.into()),
..MenuBarEntry::default()
},
MenuBarEntry {
label: "Export…".into(),
shortcut: action_keys!(DialogMessageDiscriminant::RequestExportDialog),
action: MenuBarEntry::create_action(|_| DialogMessage::RequestExportDialog.into()),
..MenuBarEntry::default()
},
],
vec![MenuBarEntry {
label: "Preferences…".into(),
icon: Some("Settings".into()),
shortcut: action_keys!(DialogMessageDiscriminant::RequestPreferencesDialog),
action: MenuBarEntry::create_action(|_| DialogMessage::RequestPreferencesDialog.into()),
..MenuBarEntry::default()
}],
]),
),
MenuBarEntry::new_root( MenuBarEntry::new_root(
"Edit".into(), "Edit".into(),
no_active_document,
MenuBarEntryChildren(vec![ MenuBarEntryChildren(vec![
vec![ vec![
MenuBarEntry { MenuBarEntry {
@ -136,6 +77,7 @@ impl PropertyHolder for MenuBarMessageHandler {
), ),
MenuBarEntry::new_root( MenuBarEntry::new_root(
"Layer".into(), "Layer".into(),
no_active_document,
MenuBarEntryChildren(vec![ MenuBarEntryChildren(vec![
vec![ vec![
MenuBarEntry { MenuBarEntry {
@ -213,6 +155,7 @@ impl PropertyHolder for MenuBarMessageHandler {
), ),
MenuBarEntry::new_root( MenuBarEntry::new_root(
"Document".into(), "Document".into(),
no_active_document,
MenuBarEntryChildren(vec![vec![MenuBarEntry { MenuBarEntryChildren(vec![vec![MenuBarEntry {
label: "Clear Artboards".into(), label: "Clear Artboards".into(),
action: MenuBarEntry::create_action(|_| ArtboardMessage::ClearArtboards.into()), action: MenuBarEntry::create_action(|_| ArtboardMessage::ClearArtboards.into()),
@ -221,6 +164,7 @@ impl PropertyHolder for MenuBarMessageHandler {
), ),
MenuBarEntry::new_root( MenuBarEntry::new_root(
"View".into(), "View".into(),
no_active_document,
MenuBarEntryChildren(vec![vec![ MenuBarEntryChildren(vec![vec![
MenuBarEntry { MenuBarEntry {
label: "Zoom to Selected".into(), label: "Zoom to Selected".into(),
@ -248,8 +192,84 @@ impl PropertyHolder for MenuBarMessageHandler {
}, },
]]), ]]),
), ),
];
let mut menu_bar_entries = vec![
MenuBarEntry {
icon: Some("GraphiteLogo".into()),
action: MenuBarEntry::create_action(|_| FrontendMessage::TriggerVisitLink { url: "https://graphite.rs".into() }.into()),
..Default::default()
},
MenuBarEntry::new_root(
"File".into(),
false,
MenuBarEntryChildren(vec![
vec![
MenuBarEntry {
label: "New…".into(),
icon: Some("File".into()),
action: MenuBarEntry::create_action(|_| DialogMessage::RequestNewDocumentDialog.into()),
shortcut: action_keys!(DialogMessageDiscriminant::RequestNewDocumentDialog),
children: MenuBarEntryChildren::empty(),
..MenuBarEntry::default()
},
MenuBarEntry {
label: "Open…".into(),
shortcut: action_keys!(PortfolioMessageDiscriminant::OpenDocument),
action: MenuBarEntry::create_action(|_| PortfolioMessage::OpenDocument.into()),
..MenuBarEntry::default()
},
],
vec![
MenuBarEntry {
label: "Close".into(),
shortcut: action_keys!(PortfolioMessageDiscriminant::CloseActiveDocumentWithConfirmation),
action: MenuBarEntry::create_action(|_| PortfolioMessage::CloseActiveDocumentWithConfirmation.into()),
disabled: no_active_document,
..MenuBarEntry::default()
},
MenuBarEntry {
label: "Close All".into(),
shortcut: action_keys!(DialogMessageDiscriminant::CloseAllDocumentsWithConfirmation),
action: MenuBarEntry::create_action(|_| DialogMessage::CloseAllDocumentsWithConfirmation.into()),
disabled: no_active_document,
..MenuBarEntry::default()
},
],
vec![MenuBarEntry {
label: "Save".into(),
shortcut: action_keys!(DocumentMessageDiscriminant::SaveDocument),
action: MenuBarEntry::create_action(|_| DocumentMessage::SaveDocument.into()),
disabled: no_active_document,
..MenuBarEntry::default()
}],
vec![
MenuBarEntry {
label: "Import…".into(),
shortcut: action_keys!(PortfolioMessageDiscriminant::Import),
action: MenuBarEntry::create_action(|_| PortfolioMessage::Import.into()),
..MenuBarEntry::default()
},
MenuBarEntry {
label: "Export…".into(),
shortcut: action_keys!(DialogMessageDiscriminant::RequestExportDialog),
action: MenuBarEntry::create_action(|_| DialogMessage::RequestExportDialog.into()),
disabled: no_active_document,
..MenuBarEntry::default()
},
],
vec![MenuBarEntry {
label: "Preferences…".into(),
icon: Some("Settings".into()),
shortcut: action_keys!(DialogMessageDiscriminant::RequestPreferencesDialog),
action: MenuBarEntry::create_action(|_| DialogMessage::RequestPreferencesDialog.into()),
..MenuBarEntry::default()
}],
]),
),
MenuBarEntry::new_root( MenuBarEntry::new_root(
"Help".into(), "Help".into(),
true,
MenuBarEntryChildren(vec![ MenuBarEntryChildren(vec![
vec![MenuBarEntry { vec![MenuBarEntry {
label: "About Graphite".into(), label: "About Graphite".into(),
@ -327,6 +347,11 @@ impl PropertyHolder for MenuBarMessageHandler {
], ],
]), ]),
), ),
])) ];
if !no_active_document {
menu_bar_entries.splice(2..2, conditional_menu_entries);
}
Layout::MenuLayout(MenuLayout::new(menu_bar_entries))
} }
} }

View File

@ -33,11 +33,13 @@ pub struct PortfolioMessageHandler {
impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &PreferencesMessageHandler)> for PortfolioMessageHandler { impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &PreferencesMessageHandler)> for PortfolioMessageHandler {
#[remain::check] #[remain::check]
fn process_message(&mut self, message: PortfolioMessage, responses: &mut VecDeque<Message>, (ipp, preferences): (&InputPreprocessorMessageHandler, &PreferencesMessageHandler)) { fn process_message(&mut self, message: PortfolioMessage, responses: &mut VecDeque<Message>, (ipp, preferences): (&InputPreprocessorMessageHandler, &PreferencesMessageHandler)) {
let has_active_document = self.active_document_id.is_some();
#[remain::sorted] #[remain::sorted]
match message { match message {
// Sub-messages // Sub-messages
#[remain::unsorted] #[remain::unsorted]
PortfolioMessage::MenuBar(message) => self.menu_bar_message_handler.process_message(message, responses, ()), PortfolioMessage::MenuBar(message) => self.menu_bar_message_handler.process_message(message, responses, has_active_document),
#[remain::unsorted] #[remain::unsorted]
PortfolioMessage::Document(message) => { PortfolioMessage::Document(message) => {
if let Some(document_id) = self.active_document_id { if let Some(document_id) = self.active_document_id {
@ -109,6 +111,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
let hint_data = HintData(vec![HintGroup(vec![])]); let hint_data = HintData(vec![HintGroup(vec![])]);
responses.add(FrontendMessage::UpdateInputHints { hint_data }); responses.add(FrontendMessage::UpdateInputHints { hint_data });
} }
// Actually delete the document (delay to delete document is required to let the document and properties panel messages above get processed) // Actually delete the document (delay to delete document is required to let the document and properties panel messages above get processed)
responses.add(PortfolioMessage::DeleteDocument { document_id }); responses.add(PortfolioMessage::DeleteDocument { document_id });
@ -179,6 +182,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
if self.document_ids.is_empty() { if self.document_ids.is_empty() {
self.active_document_id = None; self.active_document_id = None;
responses.add(MenuBarMessage::SendLayout);
} else if self.active_document_id.is_some() { } else if self.active_document_id.is_some() {
let document_id = if document_index == self.document_ids.len() { let document_id = if document_index == self.document_ids.len() {
// If we closed the last document take the one previous (same as last) // If we closed the last document take the one previous (same as last)
@ -195,6 +199,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
self.documents.clear(); self.documents.clear();
self.document_ids.clear(); self.document_ids.clear();
self.active_document_id = None; self.active_document_id = None;
responses.add(MenuBarMessage::SendLayout);
} }
PortfolioMessage::FontLoaded { PortfolioMessage::FontLoaded {
font_family, font_family,
@ -462,7 +467,10 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(PortfolioMessage::UpdateDocumentWidgets);
responses.add(NavigationMessage::TranslateCanvas { delta: (0., 0.).into() }); responses.add(NavigationMessage::TranslateCanvas { delta: (0., 0.).into() });
} }
PortfolioMessage::SetActiveDocument { document_id } => self.active_document_id = Some(document_id), PortfolioMessage::SetActiveDocument { document_id } => {
self.active_document_id = Some(document_id);
responses.add(MenuBarMessage::SendLayout);
}
PortfolioMessage::SetImageBlobUrl { PortfolioMessage::SetImageBlobUrl {
document_id, document_id,
layer_path, layer_path,

View File

@ -7,6 +7,7 @@ use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHan
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
use document_legacy::intersection::Quad; use document_legacy::intersection::Quad;
use document_legacy::layers::layer_layer::CachedOutputData;
use document_legacy::layers::style::Fill; use document_legacy::layers::style::Fill;
use glam::DVec2; use glam::DVec2;
@ -103,6 +104,17 @@ impl Fsm for FillToolFsmState {
let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]); let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]);
if let Some(path) = document.document_legacy.intersects_quad_root(quad, render_data).last() { if let Some(path) = document.document_legacy.intersects_quad_root(quad, render_data).last() {
let is_bitmap = document
.document_legacy
.layer(path)
.ok()
.and_then(|layer| layer.as_layer().ok())
.map_or(false, |layer| matches!(layer.cached_output_data, CachedOutputData::BlobURL(_) | CachedOutputData::SurfaceId(_)));
if is_bitmap {
return self;
}
let color = match lmb_or_rmb { let color = match lmb_or_rmb {
LeftPointerDown => global_tool_data.primary_color, LeftPointerDown => global_tool_data.primary_color,
RightPointerDown => global_tool_data.secondary_color, RightPointerDown => global_tool_data.secondary_color,

View File

@ -11,6 +11,7 @@ use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
use document_legacy::intersection::Quad; use document_legacy::intersection::Quad;
use document_legacy::layers::layer_info::Layer; use document_legacy::layers::layer_info::Layer;
use document_legacy::layers::layer_layer::CachedOutputData;
use document_legacy::layers::style::{Fill, Gradient, GradientType, PathStyle, RenderData, Stroke}; use document_legacy::layers::style::{Fill, Gradient, GradientType, PathStyle, RenderData, Stroke};
use document_legacy::LayerId; use document_legacy::LayerId;
use document_legacy::Operation; use document_legacy::Operation;
@ -420,9 +421,6 @@ impl Fsm for GradientToolFsmState {
} }
for path in document.selected_visible_layers() { for path in document.selected_visible_layers() {
if !document.document_legacy.multiply_transforms(path).unwrap().inverse().is_finite() {
continue;
}
let layer = document.document_legacy.layer(path).unwrap(); let layer = document.document_legacy.layer(path).unwrap();
if let Ok(Fill::Gradient(gradient)) = layer.style().map(|style| style.fill()) { if let Ok(Fill::Gradient(gradient)) = layer.style().map(|style| style.fill()) {
@ -568,7 +566,18 @@ impl Fsm for GradientToolFsmState {
let quad = Quad::from_box([input.mouse.position - tolerance, input.mouse.position + tolerance]); let quad = Quad::from_box([input.mouse.position - tolerance, input.mouse.position + tolerance]);
let intersection = document.document_legacy.intersects_quad_root(quad, render_data).pop(); let intersection = document.document_legacy.intersects_quad_root(quad, render_data).pop();
// the intersection is the layer where the gradient is being applied
if let Some(intersection) = intersection { if let Some(intersection) = intersection {
let is_bitmap = document
.document_legacy
.layer(&intersection)
.ok()
.and_then(|layer| layer.as_layer().ok())
.map_or(false, |layer| matches!(layer.cached_output_data, CachedOutputData::BlobURL(_) | CachedOutputData::SurfaceId(_)));
if is_bitmap {
return self;
}
if !document.selected_layers_contains(&intersection) { if !document.selected_layers_contains(&intersection) {
let replacement_selected_layers = vec![intersection.clone()]; let replacement_selected_layers = vec![intersection.clone()];

View File

@ -63,7 +63,7 @@
// New fields in `MenuListEntry` // New fields in `MenuListEntry`
shortcutRequiresLock: entry.shortcut ? shortcutRequiresLock(entry.shortcut.keys) : undefined, shortcutRequiresLock: entry.shortcut ? shortcutRequiresLock(entry.shortcut.keys) : undefined,
value: undefined, value: undefined,
disabled: undefined, disabled: entry.disabled ?? undefined,
font: undefined, font: undefined,
ref: undefined, ref: undefined,
}); });

View File

@ -774,6 +774,7 @@ type MenuEntryCommon = {
export type MenuBarEntry = MenuEntryCommon & { export type MenuBarEntry = MenuEntryCommon & {
action: Widget; action: Widget;
children?: MenuBarEntry[][]; children?: MenuBarEntry[][];
disabled?: boolean,
}; };
// An entry in the all-encompassing MenuList component which defines all types of menus ranging from `MenuBarInput` to `DropdownInput` widgets // An entry in the all-encompassing MenuList component which defines all types of menus ranging from `MenuBarInput` to `DropdownInput` widgets
@ -1300,6 +1301,7 @@ function createMenuLayoutRecursive(children: any[][]): MenuBarEntry[][] {
...entry, ...entry,
action: hoistWidgetHolders([entry.action])[0], action: hoistWidgetHolders([entry.action])[0],
children: entry.children ? createMenuLayoutRecursive(entry.children) : undefined, children: entry.children ? createMenuLayoutRecursive(entry.children) : undefined,
disabled: entry.disabled ?? false,
})) }))
); );
} }