Skip to content

Commit 7a57907

Browse files
Add focus document toggle (#3672)
1 parent 2be7790 commit 7a57907

7 files changed

Lines changed: 133 additions & 42 deletions

File tree

editor/src/dispatcher.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ impl Dispatcher {
232232
Message::MenuBar(message) => {
233233
let menu_bar_message_handler = &mut self.message_handlers.menu_bar_message_handler;
234234

235+
menu_bar_message_handler.focus_document = self.message_handlers.portfolio_message_handler.focus_document;
235236
menu_bar_message_handler.data_panel_open = self.message_handlers.portfolio_message_handler.data_panel_open;
236237
menu_bar_message_handler.layers_panel_open = self.message_handlers.portfolio_message_handler.layers_panel_open;
237238
menu_bar_message_handler.properties_panel_open = self.message_handlers.portfolio_message_handler.properties_panel_open;

editor/src/messages/input_mapper/input_mappings.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,8 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping {
334334
entry!(KeyDown(KeyX); modifiers=[Shift], action_dispatch=ToolMessage::SwapColors),
335335
entry!(KeyDown(KeyC); modifiers=[Alt], action_dispatch=ToolMessage::SelectRandomWorkingColor { primary: true }),
336336
entry!(KeyDown(KeyC); modifiers=[Alt, Shift], action_dispatch=ToolMessage::SelectRandomWorkingColor { primary: false }),
337-
entry!(KeyDownNoRepeat(Tab); action_dispatch=ToolMessage::ToggleSelectVsPath),
337+
// TODO: Change to KeyDownNoRepeat when https://github.com/GraphiteEditor/Graphite/issues/2266 is resolved
338+
entry!(KeyDown(Tab); action_dispatch=ToolMessage::ToggleSelectVsPath),
338339
//
339340
// DocumentMessage
340341
entry!(KeyDown(Space); modifiers=[Control], action_dispatch=DocumentMessage::GraphViewOverlayToggle),
@@ -448,6 +449,7 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping {
448449
entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=PortfolioMessage::Copy { clipboard: Clipboard::Device }),
449450
entry!(KeyDown(KeyR); modifiers=[Alt], action_dispatch=PortfolioMessage::ToggleRulers),
450451
entry!(KeyDown(KeyD); modifiers=[Alt], action_dispatch=PortfolioMessage::ToggleDataPanelOpen),
452+
entry!(KeyDown(Enter); modifiers=[Alt], action_dispatch=PortfolioMessage::ToggleFocusDocument),
451453
//
452454
// DialogMessage
453455
entry!(KeyDown(KeyE); modifiers=[Accel], action_dispatch=DialogMessage::RequestExportDialog),

editor/src/messages/menu_bar/menu_bar_message_handler.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub struct MenuBarMessageHandler {
2121
pub data_panel_open: bool,
2222
pub layers_panel_open: bool,
2323
pub properties_panel_open: bool,
24+
pub focus_document: bool,
2425
}
2526

2627
#[message_handler_data]
@@ -628,23 +629,31 @@ impl LayoutHolder for MenuBarMessageHandler {
628629
.icon("FullscreenEnter")
629630
.tooltip_shortcut(action_shortcut!(AppWindowMessageDiscriminant::Fullscreen))
630631
.on_commit(|_| AppWindowMessage::Fullscreen.into()),
632+
MenuListEntry::new("Focus Document")
633+
.label("Focus Document")
634+
.icon(if self.focus_document { "CheckboxChecked" } else { "CheckboxUnchecked" })
635+
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::ToggleFocusDocument))
636+
.on_commit(|_| PortfolioMessage::ToggleFocusDocument.into()),
631637
],
632638
vec![
633639
MenuListEntry::new("Properties")
634640
.label("Properties")
635641
.icon(if self.properties_panel_open { "CheckboxChecked" } else { "CheckboxUnchecked" })
636642
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::TogglePropertiesPanelOpen))
637-
.on_commit(|_| PortfolioMessage::TogglePropertiesPanelOpen.into()),
643+
.on_commit(|_| PortfolioMessage::TogglePropertiesPanelOpen.into())
644+
.disabled(self.focus_document),
638645
MenuListEntry::new("Layers")
639646
.label("Layers")
640647
.icon(if self.layers_panel_open { "CheckboxChecked" } else { "CheckboxUnchecked" })
641648
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::ToggleLayersPanelOpen))
642-
.on_commit(|_| PortfolioMessage::ToggleLayersPanelOpen.into()),
649+
.on_commit(|_| PortfolioMessage::ToggleLayersPanelOpen.into())
650+
.disabled(self.focus_document),
643651
MenuListEntry::new("Data")
644652
.label("Data")
645653
.icon(if self.data_panel_open { "CheckboxChecked" } else { "CheckboxUnchecked" })
646654
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::ToggleDataPanelOpen))
647-
.on_commit(|_| PortfolioMessage::ToggleDataPanelOpen.into()),
655+
.on_commit(|_| PortfolioMessage::ToggleDataPanelOpen.into())
656+
.disabled(self.focus_document),
648657
],
649658
])
650659
.widget_instance(),

editor/src/messages/portfolio/document/document_message_handler.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,10 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
316316
}
317317
DocumentMessage::ClearLayersPanel => {
318318
// Send an empty layer list
319-
let data_buffer: RawBuffer = Self::default().serialize_root();
320-
responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer });
319+
if layers_panel_open {
320+
let data_buffer: RawBuffer = Self::default().serialize_root();
321+
responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer });
322+
}
321323

322324
// Clear the control bar
323325
responses.add(LayoutMessage::SendLayout {

editor/src/messages/portfolio/portfolio_message.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ pub enum PortfolioMessage {
149149
ignore_hash: bool,
150150
},
151151
ToggleResetNodesToDefinitionsOnOpen,
152+
ToggleFocusDocument,
152153
ToggleDataPanelOpen,
153154
TogglePropertiesPanelOpen,
154155
ToggleLayersPanelOpen,

editor/src/messages/portfolio/portfolio_message_handler.rs

Lines changed: 105 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,12 @@ pub struct PortfolioMessageHandler {
6060
pub executor: NodeGraphExecutor,
6161
pub selection_mode: SelectionMode,
6262
pub reset_node_definitions_on_open: bool,
63-
pub data_panel_open: bool,
64-
#[derivative(Default(value = "true"))]
65-
pub layers_panel_open: bool,
63+
pub focus_document: bool,
6664
#[derivative(Default(value = "true"))]
6765
pub properties_panel_open: bool,
66+
#[derivative(Default(value = "true"))]
67+
pub layers_panel_open: bool,
68+
pub data_panel_open: bool,
6869
}
6970

7071
#[message_handler_data]
@@ -94,9 +95,9 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
9495
current_tool,
9596
preferences,
9697
viewport,
97-
data_panel_open: self.data_panel_open,
98-
layers_panel_open: self.layers_panel_open,
99-
properties_panel_open: self.properties_panel_open,
98+
data_panel_open: self.data_panel_open && !self.focus_document,
99+
layers_panel_open: self.layers_panel_open && !self.focus_document,
100+
properties_panel_open: self.properties_panel_open && !self.focus_document,
100101
};
101102
document.process_message(message, responses, document_inputs)
102103
}
@@ -158,9 +159,9 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
158159
current_tool,
159160
preferences,
160161
viewport,
161-
data_panel_open: self.data_panel_open,
162-
layers_panel_open: self.layers_panel_open,
163-
properties_panel_open: self.properties_panel_open,
162+
data_panel_open: self.data_panel_open && !self.focus_document,
163+
layers_panel_open: self.layers_panel_open && !self.focus_document,
164+
properties_panel_open: self.properties_panel_open && !self.focus_document,
164165
};
165166
document.process_message(message, responses, document_inputs)
166167
}
@@ -452,7 +453,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
452453
responses.add(NavigationMessage::CanvasPan { delta: (0., 0.).into() });
453454
}
454455

455-
self.load_document(new_document, document_id, self.layers_panel_open, responses, false);
456+
self.load_document(new_document, document_id, responses, false);
456457
responses.add(PortfolioMessage::SelectDocument { document_id });
457458
}
458459
PortfolioMessage::NextDocument => {
@@ -655,7 +656,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
655656
}
656657

657658
// Load the document into the portfolio so it opens in the editor
658-
self.load_document(document, document_id, self.layers_panel_open, responses, to_front);
659+
self.load_document(document, document_id, responses, to_front);
659660

660661
if select_after_open {
661662
responses.add(PortfolioMessage::SelectDocument { document_id });
@@ -1150,7 +1151,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
11501151
let node_to_inspect = self.node_to_inspect();
11511152

11521153
let Some(document) = self.documents.get_mut(&document_id) else {
1153-
log::error!("Tried to render non-existent document");
1154+
log::error!("Tried to render non-existent document {:?}", document_id);
11541155
return;
11551156
};
11561157

@@ -1177,38 +1178,74 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
11771178
Ok(message) => responses.add_front(message),
11781179
}
11791180
}
1180-
PortfolioMessage::ToggleDataPanelOpen => {
1181-
self.data_panel_open = !self.data_panel_open;
1181+
PortfolioMessage::ToggleFocusDocument => {
1182+
self.focus_document = !self.focus_document;
11821183
responses.add(MenuBarMessage::SendLayout);
11831184

1184-
// Run the graph to grab the data
1185-
if self.data_panel_open {
1186-
// When opening, we make the frontend show the panel first so it can start receiving its message subscriptions for the data it will display
1187-
responses.add(FrontendMessage::UpdateDataPanelState { open: self.data_panel_open });
1185+
if self.focus_document {
1186+
if self.properties_panel_open {
1187+
responses.add(PropertiesPanelMessage::Clear);
1188+
responses.add(FrontendMessage::UpdatePropertiesPanelState { open: false });
1189+
}
11881190

1189-
responses.add(NodeGraphMessage::RunDocumentGraph);
1191+
if self.layers_panel_open {
1192+
responses.add(DocumentMessage::ClearLayersPanel);
1193+
responses.add(FrontendMessage::UpdateLayersPanelState { open: false });
1194+
}
1195+
1196+
if self.data_panel_open {
1197+
responses.add(DataPanelMessage::ClearLayout);
1198+
responses.add(FrontendMessage::UpdateDataPanelState { open: false });
1199+
}
11901200
} else {
1191-
// If we don't clear the panel, the layout diffing system will assume widgets still exist when it attempts to update the data panel next time it is opened
1192-
responses.add(DataPanelMessage::ClearLayout);
1201+
if self.properties_panel_open {
1202+
responses.add(FrontendMessage::UpdatePropertiesPanelState { open: true });
1203+
}
1204+
if self.layers_panel_open {
1205+
responses.add(FrontendMessage::UpdateLayersPanelState { open: true });
1206+
}
1207+
if self.data_panel_open {
1208+
responses.add(FrontendMessage::UpdateDataPanelState { open: true });
1209+
}
11931210

1194-
// When closing, we make the frontend hide the panel last so it can finish receiving its message subscriptions before it is destroyed
1195-
responses.add(FrontendMessage::UpdateDataPanelState { open: self.data_panel_open });
1211+
// Run the graph to grab the data
1212+
if self.properties_panel_open || self.layers_panel_open || self.data_panel_open {
1213+
responses.add(NodeGraphMessage::RunDocumentGraph);
1214+
}
1215+
1216+
if self.properties_panel_open {
1217+
responses.add(PropertiesPanelMessage::Refresh);
1218+
}
1219+
if self.layers_panel_open && self.active_document_id.is_some() {
1220+
responses.add(DeferMessage::AfterGraphRun {
1221+
messages: vec![NodeGraphMessage::UpdateLayerPanel.into(), DocumentMessage::DocumentStructureChanged.into()],
1222+
});
1223+
}
11961224
}
11971225
}
11981226
PortfolioMessage::TogglePropertiesPanelOpen => {
1227+
if self.focus_document {
1228+
return;
1229+
}
1230+
11991231
self.properties_panel_open = !self.properties_panel_open;
12001232
responses.add(MenuBarMessage::SendLayout);
12011233

1202-
responses.add(FrontendMessage::UpdatePropertiesPanelState { open: self.properties_panel_open });
1203-
12041234
// Run the graph to grab the data
12051235
if self.properties_panel_open {
1236+
responses.add(FrontendMessage::UpdatePropertiesPanelState { open: self.properties_panel_open });
12061237
responses.add(NodeGraphMessage::RunDocumentGraph);
1238+
responses.add(PropertiesPanelMessage::Refresh);
1239+
} else {
1240+
responses.add(PropertiesPanelMessage::Clear);
1241+
responses.add(FrontendMessage::UpdatePropertiesPanelState { open: self.properties_panel_open });
12071242
}
1208-
1209-
responses.add(PropertiesPanelMessage::Refresh);
12101243
}
12111244
PortfolioMessage::ToggleLayersPanelOpen => {
1245+
if self.focus_document {
1246+
return;
1247+
}
1248+
12121249
self.layers_panel_open = !self.layers_panel_open;
12131250
responses.add(MenuBarMessage::SendLayout);
12141251

@@ -1218,9 +1255,11 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
12181255
responses.add(FrontendMessage::UpdateLayersPanelState { open: self.layers_panel_open });
12191256

12201257
responses.add(NodeGraphMessage::RunDocumentGraph);
1221-
responses.add(DeferMessage::AfterGraphRun {
1222-
messages: vec![NodeGraphMessage::UpdateLayerPanel.into(), DocumentMessage::DocumentStructureChanged.into()],
1223-
});
1258+
if self.active_document_id.is_some() {
1259+
responses.add(DeferMessage::AfterGraphRun {
1260+
messages: vec![NodeGraphMessage::UpdateLayerPanel.into(), DocumentMessage::DocumentStructureChanged.into()],
1261+
});
1262+
}
12241263
} else {
12251264
// If we don't clear the panel, the layout diffing system will assume widgets still exist when it attempts to update the layers panel next time it is opened
12261265
responses.add(DocumentMessage::ClearLayersPanel);
@@ -1229,6 +1268,28 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
12291268
responses.add(FrontendMessage::UpdateLayersPanelState { open: self.layers_panel_open });
12301269
}
12311270
}
1271+
PortfolioMessage::ToggleDataPanelOpen => {
1272+
if self.focus_document {
1273+
return;
1274+
}
1275+
1276+
self.data_panel_open = !self.data_panel_open;
1277+
responses.add(MenuBarMessage::SendLayout);
1278+
1279+
// Run the graph to grab the data
1280+
if self.data_panel_open {
1281+
// When opening, we make the frontend show the panel first so it can start receiving its message subscriptions for the data it will display
1282+
responses.add(FrontendMessage::UpdateDataPanelState { open: self.data_panel_open });
1283+
1284+
responses.add(NodeGraphMessage::RunDocumentGraph);
1285+
} else {
1286+
// If we don't clear the panel, the layout diffing system will assume widgets still exist when it attempts to update the data panel next time it is opened
1287+
responses.add(DataPanelMessage::ClearLayout);
1288+
1289+
// When closing, we make the frontend hide the panel last so it can finish receiving its message subscriptions before it is destroyed
1290+
responses.add(FrontendMessage::UpdateDataPanelState { open: self.data_panel_open });
1291+
}
1292+
}
12321293
PortfolioMessage::ToggleRulers => {
12331294
if let Some(document) = self.active_document_mut() {
12341295
document.rulers_visible = !document.rulers_visible;
@@ -1280,7 +1341,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
12801341
fn actions(&self) -> ActionList {
12811342
let mut common = actions!(PortfolioMessageDiscriminant;
12821343
Open,
1283-
ToggleDataPanelOpen,
1344+
ToggleFocusDocument,
12841345
);
12851346

12861347
// Extend with actions that require an active document
@@ -1305,6 +1366,15 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
13051366
}
13061367
}
13071368

1369+
// Extend with actions that are disabled when focusing the document
1370+
if !self.focus_document {
1371+
common.extend(actions!(PortfolioMessageDiscriminant;
1372+
TogglePropertiesPanelOpen,
1373+
ToggleLayersPanelOpen,
1374+
ToggleDataPanelOpen,
1375+
));
1376+
}
1377+
13081378
common
13091379
}
13101380
}
@@ -1385,14 +1455,14 @@ impl PortfolioMessageHandler {
13851455
}
13861456
}
13871457

1388-
fn load_document(&mut self, mut new_document: DocumentMessageHandler, document_id: DocumentId, layers_panel_open: bool, responses: &mut VecDeque<Message>, to_front: bool) {
1458+
fn load_document(&mut self, mut new_document: DocumentMessageHandler, document_id: DocumentId, responses: &mut VecDeque<Message>, to_front: bool) {
13891459
if to_front {
13901460
self.document_ids.push_front(document_id);
13911461
} else {
13921462
self.document_ids.push_back(document_id);
13931463
}
1394-
new_document.update_layers_panel_control_bar_widgets(layers_panel_open, responses);
1395-
new_document.update_layers_panel_bottom_bar_widgets(layers_panel_open, responses);
1464+
new_document.update_layers_panel_control_bar_widgets(self.layers_panel_open && !self.focus_document, responses);
1465+
new_document.update_layers_panel_bottom_bar_widgets(self.layers_panel_open && !self.focus_document, responses);
13961466

13971467
self.documents.insert(document_id, new_document);
13981468

@@ -1439,7 +1509,7 @@ impl PortfolioMessageHandler {
14391509
/// Get the ID of the selected node that should be used as the current source for the Data panel.
14401510
pub fn node_to_inspect(&self) -> Option<NodeId> {
14411511
// Skip if the Data panel is not open
1442-
if !self.data_panel_open {
1512+
if !self.data_panel_open || self.focus_document {
14431513
return None;
14441514
}
14451515

frontend/src/io-managers/input.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,13 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
9393

9494
// Don't redirect tab or enter if not in canvas (to allow navigating elements)
9595
potentiallyRestoreCanvasFocus(e);
96-
if (!canvasFocused && !targetIsTextField(e.target || undefined) && ["Tab", "Enter", "NumpadEnter", "Space", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowUp"].includes(key)) return false;
96+
if (
97+
!canvasFocused &&
98+
!targetIsTextField(e.target || undefined) &&
99+
["Tab", "Enter", "NumpadEnter", "Space", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowUp"].includes(key) &&
100+
!(e.ctrlKey || e.metaKey || e.altKey)
101+
)
102+
return false;
97103

98104
// Don't redirect if a MenuList is open
99105
if (window.document.querySelector("[data-floating-menu-content]")) return false;

0 commit comments

Comments
 (0)