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:
parent
4fdf8410cf
commit
4fead6e7ec
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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 => {
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}],
|
}],
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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}>
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue