Add option to toggle ruler visibility (#1479)

* Sending Toggle Ruler message and setting visibility. Text and markers not working

* Added resize on mount for Ruler Input

* Set default for rulers_visible to pass test

* Ruler Visibility portfolio wide instead of document

* Sending Toggle Ruler message and setting visibility. Text and markers not working

* Ruler Visibility portfolio wide instead of document

* Cleanup

* Reorganize the View menu bar; add toggle rulers hotkey

* Remove non-working redundant bools, and make rulers set per-document

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Bijay Shrestha 2023-11-28 17:19:23 +05:45 committed by GitHub
parent 4fdf8410cf
commit 4fead6e7ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 68 additions and 17 deletions

View File

@ -173,6 +173,7 @@ pub enum FrontendMessage {
origin: (f64, f64), origin: (f64, f64),
spacing: f64, spacing: f64,
interval: f64, interval: f64,
visible: bool,
}, },
UpdateDocumentScrollbars { UpdateDocumentScrollbars {
position: (f64, f64), position: (f64, f64),

View File

@ -343,6 +343,7 @@ pub fn default_mapping() -> Mapping {
entry!(KeyDown(KeyI); modifiers=[Accel], action_dispatch=PortfolioMessage::Import), entry!(KeyDown(KeyI); modifiers=[Accel], action_dispatch=PortfolioMessage::Import),
entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=PortfolioMessage::Cut { clipboard: Clipboard::Device }), entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=PortfolioMessage::Cut { clipboard: Clipboard::Device }),
entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=PortfolioMessage::Copy { clipboard: Clipboard::Device }), entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=PortfolioMessage::Copy { clipboard: Clipboard::Device }),
entry!(KeyDown(KeyR); modifiers=[Alt], action_dispatch=PortfolioMessage::ToggleRulers),
// //
// FrontendMessage // FrontendMessage
entry!(KeyDown(KeyV); modifiers=[Accel], action_dispatch=FrontendMessage::TriggerPaste), entry!(KeyDown(KeyV); modifiers=[Accel], action_dispatch=FrontendMessage::TriggerPaste),

View File

@ -30,6 +30,12 @@ use graphene_core::raster::ImageFrame;
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Utility function for providing a default boolean value to serde.
#[inline(always)]
fn return_true() -> bool {
true
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DocumentMessageHandler { pub struct DocumentMessageHandler {
pub document_legacy: DocumentLegacy, pub document_legacy: DocumentLegacy,
@ -43,6 +49,8 @@ pub struct DocumentMessageHandler {
#[serde(skip)] #[serde(skip)]
pub snapping_state: SnappingState, pub snapping_state: SnappingState,
pub overlays_visible: bool, pub overlays_visible: bool,
#[serde(default = "return_true")]
pub rulers_visible: bool,
#[serde(skip)] #[serde(skip)]
pub document_undo_history: VecDeque<DocumentSave>, pub document_undo_history: VecDeque<DocumentSave>,
@ -81,6 +89,7 @@ impl Default for DocumentMessageHandler {
view_mode: ViewMode::default(), view_mode: ViewMode::default(),
snapping_state: SnappingState::default(), snapping_state: SnappingState::default(),
overlays_visible: true, overlays_visible: true,
rulers_visible: true,
document_undo_history: VecDeque::new(), document_undo_history: VecDeque::new(),
document_redo_history: VecDeque::new(), document_redo_history: VecDeque::new(),
@ -614,6 +623,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
origin: ruler_origin.into(), origin: ruler_origin.into(),
spacing: ruler_spacing, spacing: ruler_spacing,
interval: ruler_interval, interval: ruler_interval,
visible: self.rulers_visible,
}); });
} }
RenderScrollbars => { RenderScrollbars => {

View File

@ -6,14 +6,15 @@ use crate::messages::prelude::*;
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct MenuBarMessageHandler { pub struct MenuBarMessageHandler {
no_active_document: bool, no_active_document: bool,
rulers_hidden: bool,
} }
impl MessageHandler<MenuBarMessage, bool> for MenuBarMessageHandler { impl MessageHandler<MenuBarMessage, (bool, bool)> for MenuBarMessageHandler {
#[remain::check] #[remain::check]
fn process_message(&mut self, message: MenuBarMessage, responses: &mut VecDeque<Message>, has_active_document: bool) { fn process_message(&mut self, message: MenuBarMessage, responses: &mut VecDeque<Message>, (has_active_document, rulers_hidden): (bool, bool)) {
use MenuBarMessage::*; use MenuBarMessage::*;
self.no_active_document = !has_active_document; self.no_active_document = !has_active_document;
self.rulers_hidden = rulers_hidden;
#[remain::sorted] #[remain::sorted]
match message { match message {
SendLayout => self.send_layout(responses, LayoutTarget::MenuBar), SendLayout => self.send_layout(responses, LayoutTarget::MenuBar),
@ -289,8 +290,17 @@ impl LayoutHolder for MenuBarMessageHandler {
disabled: no_active_document, disabled: no_active_document,
..MenuBarEntry::default() ..MenuBarEntry::default()
}, },
],
vec![
MenuBarEntry { MenuBarEntry {
label: "Zoom to Fit".into(), label: "Zoom to Fit Selection".into(),
shortcut: action_keys!(NavigationMessageDiscriminant::FitViewportToSelection),
action: MenuBarEntry::create_action(|_| NavigationMessage::FitViewportToSelection.into()),
disabled: no_active_document,
..MenuBarEntry::default()
},
MenuBarEntry {
label: "Zoom to Fit All".into(),
shortcut: action_keys!(DocumentMessageDiscriminant::ZoomCanvasToFitAll), shortcut: action_keys!(DocumentMessageDiscriminant::ZoomCanvasToFitAll),
action: MenuBarEntry::create_action(|_| DocumentMessage::ZoomCanvasToFitAll.into()), action: MenuBarEntry::create_action(|_| DocumentMessage::ZoomCanvasToFitAll.into()),
disabled: no_active_document, disabled: no_active_document,
@ -312,9 +322,10 @@ impl LayoutHolder for MenuBarMessageHandler {
}, },
], ],
vec![MenuBarEntry { vec![MenuBarEntry {
label: "Frame Selected".into(), label: "Rulers".into(),
shortcut: action_keys!(NavigationMessageDiscriminant::FitViewportToSelection), icon: Some(if self.rulers_hidden { "CheckboxUnchecked" } else { "CheckboxChecked" }.into()),
action: MenuBarEntry::create_action(|_| NavigationMessage::FitViewportToSelection.into()), shortcut: action_keys!(PortfolioMessageDiscriminant::ToggleRulers),
action: MenuBarEntry::create_action(|_| PortfolioMessage::ToggleRulers.into()),
disabled: no_active_document, disabled: no_active_document,
..MenuBarEntry::default() ..MenuBarEntry::default()
}], }],

View File

@ -114,6 +114,7 @@ pub enum PortfolioMessage {
document_id: u64, document_id: u64,
layer_path: Vec<LayerId>, layer_path: Vec<LayerId>,
}, },
ToggleRulers,
UpdateDocumentWidgets, UpdateDocumentWidgets,
UpdateOpenDocumentsList, UpdateOpenDocumentsList,
} }

View File

@ -38,7 +38,13 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
match message { match message {
// Sub-messages // Sub-messages
#[remain::unsorted] #[remain::unsorted]
PortfolioMessage::MenuBar(message) => self.menu_bar_message_handler.process_message(message, responses, has_active_document), PortfolioMessage::MenuBar(message) => {
if let Some(document_id) = self.active_document_id {
if let Some(document) = self.documents.get_mut(&document_id) {
self.menu_bar_message_handler.process_message(message, responses, (has_active_document, document.rulers_visible));
}
}
}
#[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 {
@ -517,6 +523,14 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
}); });
} }
} }
PortfolioMessage::ToggleRulers => {
if let Some(document) = self.active_document_mut() {
document.rulers_visible = !document.rulers_visible;
responses.add(DocumentMessage::RenderRulers);
responses.add(MenuBarMessage::SendLayout);
}
}
PortfolioMessage::UpdateDocumentWidgets => { PortfolioMessage::UpdateDocumentWidgets => {
if let Some(document) = self.active_document() { if let Some(document) = self.active_document() {
document.update_document_widgets(responses); document.update_document_widgets(responses);
@ -552,6 +566,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
OpenDocument, OpenDocument,
PasteIntoFolder, PasteIntoFolder,
PrevDocument, PrevDocument,
ToggleRulers,
); );
if self.graph_view_overlay_open { if self.graph_view_overlay_open {

View File

@ -57,6 +57,7 @@
let rulerOrigin: XY = { x: 0, y: 0 }; let rulerOrigin: XY = { x: 0, y: 0 };
let rulerSpacing = 100; let rulerSpacing = 100;
let rulerInterval = 100; let rulerInterval = 100;
let rulersVisible = true;
// Rendered SVG viewport data // Rendered SVG viewport data
let artworkSvg = ""; let artworkSvg = "";
@ -213,10 +214,11 @@
scrollbarMultiplier = multiplier; scrollbarMultiplier = multiplier;
} }
export function updateDocumentRulers(origin: XY, spacing: number, interval: number) { export function updateDocumentRulers(origin: XY, spacing: number, interval: number, visible: boolean) {
rulerOrigin = origin; rulerOrigin = origin;
rulerSpacing = spacing; rulerSpacing = spacing;
rulerInterval = interval; rulerInterval = interval;
rulersVisible = visible;
} }
// Update mouse cursor icon // Update mouse cursor icon
@ -371,8 +373,8 @@
editor.subscriptions.subscribeJsMessage(UpdateDocumentRulers, async (data) => { editor.subscriptions.subscribeJsMessage(UpdateDocumentRulers, async (data) => {
await tick(); await tick();
const { origin, spacing, interval } = data; const { origin, spacing, interval, visible } = data;
updateDocumentRulers(origin, spacing, interval); updateDocumentRulers(origin, spacing, interval, visible);
}); });
// Update mouse cursor icon // Update mouse cursor icon
@ -440,13 +442,17 @@
</LayoutCol> </LayoutCol>
</LayoutCol> </LayoutCol>
<LayoutCol class="table"> <LayoutCol class="table">
<LayoutRow class="ruler-or-scrollbar top-ruler"> {#if rulersVisible}
<RulerInput origin={rulerOrigin.x} majorMarkSpacing={rulerSpacing} numberInterval={rulerInterval} direction="Horizontal" bind:this={rulerHorizontal} /> <LayoutRow class="ruler-or-scrollbar top-ruler">
</LayoutRow> <RulerInput origin={rulerOrigin.x} majorMarkSpacing={rulerSpacing} numberInterval={rulerInterval} direction="Horizontal" bind:this={rulerHorizontal} />
</LayoutRow>
{/if}
<LayoutRow class="viewport-container"> <LayoutRow class="viewport-container">
<LayoutCol class="ruler-or-scrollbar"> {#if rulersVisible}
<RulerInput origin={rulerOrigin.y} majorMarkSpacing={rulerSpacing} numberInterval={rulerInterval} direction="Vertical" bind:this={rulerVertical} /> <LayoutCol class="ruler-or-scrollbar">
</LayoutCol> <RulerInput origin={rulerOrigin.y} majorMarkSpacing={rulerSpacing} numberInterval={rulerInterval} direction="Vertical" bind:this={rulerVertical} />
</LayoutCol>
{/if}
<LayoutCol class="viewport-container" styles={{ cursor: canvasCursor }}> <LayoutCol class="viewport-container" styles={{ cursor: canvasCursor }}>
{#if cursorEyedropper} {#if cursorEyedropper}
<EyedropperPreview <EyedropperPreview

View File

@ -3,6 +3,8 @@
</script> </script>
<script lang="ts"> <script lang="ts">
import { onMount } from "svelte";
const RULER_THICKNESS = 16; const RULER_THICKNESS = 16;
const MAJOR_MARK_THICKNESS = 16; const MAJOR_MARK_THICKNESS = 16;
const MEDIUM_MARK_THICKNESS = 6; const MEDIUM_MARK_THICKNESS = 6;
@ -96,6 +98,8 @@
const remainder = n % m; const remainder = n % m;
return Math.floor(remainder >= 0 ? remainder : remainder + m); return Math.floor(remainder >= 0 ? remainder : remainder + m);
} }
onMount(resize);
</script> </script>
<div class={`ruler-input ${direction.toLowerCase()}`} bind:this={rulerInput}> <div class={`ruler-input ${direction.toLowerCase()}`} bind:this={rulerInput}>

View File

@ -473,6 +473,8 @@ export class UpdateDocumentRulers extends JsMessage {
readonly spacing!: number; readonly spacing!: number;
readonly interval!: number; readonly interval!: number;
readonly visible!: boolean;
} }
export class UpdateEyedropperSamplingState extends JsMessage { export class UpdateEyedropperSamplingState extends JsMessage {