Make scrollbars interactable (#328)
* Make scrollbars interactable * Add watcher for position change * Fix case of data * Fix updateHandlePosition capitalization * Clean up class name thing * Scroll bars between 0 and 1 * Allow width to be 100% * Scrollbars reflect backend * Include viewport in scrollbar * Add half viewport padding for scrollbars * Refactor scrollbar using lerp * Send messages to backend * Refactor * Use glam::DVec2 * Remove glam:: * Remove unnecessary abs * Add TrueDoctor's change * Add missing minus * Fix vue issues * Fix viewport size * Remove unnecessary log * Linear dragging
This commit is contained in:
parent
5c36242aeb
commit
f63b0abfde
|
|
@ -27,6 +27,7 @@ impl Dispatcher {
|
||||||
| Message::InputMapper(_)
|
| Message::InputMapper(_)
|
||||||
| Message::Documents(DocumentsMessage::Document(DocumentMessage::RenderDocument))
|
| Message::Documents(DocumentsMessage::Document(DocumentMessage::RenderDocument))
|
||||||
| Message::Frontend(FrontendMessage::UpdateCanvas { .. })
|
| Message::Frontend(FrontendMessage::UpdateCanvas { .. })
|
||||||
|
| Message::Frontend(FrontendMessage::UpdateScrollbars { .. })
|
||||||
| Message::Frontend(FrontendMessage::SetCanvasZoom { .. })
|
| Message::Frontend(FrontendMessage::SetCanvasZoom { .. })
|
||||||
| Message::Frontend(FrontendMessage::SetCanvasRotation { .. })
|
| Message::Frontend(FrontendMessage::SetCanvasRotation { .. })
|
||||||
| Message::Documents(DocumentsMessage::Document(DocumentMessage::DispatchOperation { .. }))
|
| Message::Documents(DocumentsMessage::Document(DocumentMessage::DispatchOperation { .. }))
|
||||||
|
|
|
||||||
|
|
@ -109,12 +109,6 @@ impl From<DocumentOperation> for Message {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocumentMessageHandler {
|
impl DocumentMessageHandler {
|
||||||
pub fn active_document(&self) -> &DocumentMessageHandler {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn active_document_mut(&mut self) -> &mut DocumentMessageHandler {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
fn filter_document_responses(&self, document_responses: &mut Vec<DocumentResponse>) -> bool {
|
fn filter_document_responses(&self, document_responses: &mut Vec<DocumentResponse>) -> bool {
|
||||||
let len = document_responses.len();
|
let len = document_responses.len();
|
||||||
document_responses.retain(|response| !matches!(response, DocumentResponse::DocumentChanged));
|
document_responses.retain(|response| !matches!(response, DocumentResponse::DocumentChanged));
|
||||||
|
|
@ -320,9 +314,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SetBlendModeForSelectedLayers(blend_mode) => {
|
SetBlendModeForSelectedLayers(blend_mode) => {
|
||||||
let active_document = self;
|
for path in self.layer_data.iter().filter_map(|(path, data)| data.selected.then(|| path.clone())) {
|
||||||
|
|
||||||
for path in active_document.layer_data.iter().filter_map(|(path, data)| data.selected.then(|| path.clone())) {
|
|
||||||
responses.push_back(DocumentOperation::SetLayerBlendMode { path, blend_mode }.into());
|
responses.push_back(DocumentOperation::SetLayerBlendMode { path, blend_mode }.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -407,12 +399,32 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
|
||||||
Err(e) => log::error!("DocumentError: {:?}", e),
|
Err(e) => log::error!("DocumentError: {:?}", e),
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
},
|
},
|
||||||
RenderDocument => responses.push_back(
|
RenderDocument => {
|
||||||
FrontendMessage::UpdateCanvas {
|
responses.push_back(
|
||||||
document: self.document.render_root(),
|
FrontendMessage::UpdateCanvas {
|
||||||
}
|
document: self.document.render_root(),
|
||||||
.into(),
|
}
|
||||||
),
|
.into(),
|
||||||
|
);
|
||||||
|
let root = self.layerdata(&[]);
|
||||||
|
let viewport = ipp.viewport_bounds.size();
|
||||||
|
let [bounds1, bounds2] = self.document.visible_layers_bounding_box().unwrap_or_default();
|
||||||
|
let bounds1 = bounds1.min(DVec2::ZERO) - viewport * (f64::powf(2., root.scale / 3.) * 0.5);
|
||||||
|
let bounds2 = bounds2.max(viewport) + viewport * (f64::powf(2., root.scale / 3.) * 0.5);
|
||||||
|
let bounds_length = bounds2 - bounds1;
|
||||||
|
let scrollbar_multiplier = bounds_length - viewport;
|
||||||
|
let scrollbar_position = bounds1.abs() / scrollbar_multiplier;
|
||||||
|
let scrollbar_size = viewport / bounds_length;
|
||||||
|
responses.push_back(
|
||||||
|
FrontendMessage::UpdateScrollbars {
|
||||||
|
position: scrollbar_position.into(),
|
||||||
|
size: scrollbar_size.into(),
|
||||||
|
multiplier: scrollbar_multiplier.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
NudgeSelectedLayers(x, y) => {
|
NudgeSelectedLayers(x, y) => {
|
||||||
for path in self.selected_layers().cloned() {
|
for path in self.selected_layers().cloned() {
|
||||||
let operation = DocumentOperation::TransformLayerInViewport {
|
let operation = DocumentOperation::TransformLayerInViewport {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ pub enum MovementMessage {
|
||||||
DecreaseCanvasZoom,
|
DecreaseCanvasZoom,
|
||||||
WheelCanvasZoom,
|
WheelCanvasZoom,
|
||||||
ZoomCanvasToFitAll,
|
ZoomCanvasToFitAll,
|
||||||
|
TranslateCanvas(glam::DVec2),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
#[derive(Debug, Clone, Default, PartialEq)]
|
||||||
|
|
@ -189,6 +190,12 @@ impl MessageHandler<MovementMessage, (&mut LayerData, &Document, &InputPreproces
|
||||||
self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_bounds, responses);
|
self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_bounds, responses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TranslateCanvas(delta) => {
|
||||||
|
let transformed_delta = document.root.transform.inverse().transform_vector2(delta);
|
||||||
|
|
||||||
|
layerdata.translation += transformed_delta;
|
||||||
|
self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_bounds, responses);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn actions(&self) -> ActionList {
|
fn actions(&self) -> ActionList {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ pub enum FrontendMessage {
|
||||||
DisplayConfirmationToCloseDocument { document_index: usize },
|
DisplayConfirmationToCloseDocument { document_index: usize },
|
||||||
DisplayConfirmationToCloseAllDocuments,
|
DisplayConfirmationToCloseAllDocuments,
|
||||||
UpdateCanvas { document: String },
|
UpdateCanvas { document: String },
|
||||||
|
UpdateScrollbars { position: (f64, f64), size: (f64, f64), multiplier: (f64, f64) },
|
||||||
UpdateLayer { path: Vec<LayerId>, data: LayerPanelEntry },
|
UpdateLayer { path: Vec<LayerId>, data: LayerPanelEntry },
|
||||||
ExportDocument { document: String, name: String },
|
ExportDocument { document: String, name: String },
|
||||||
SaveDocument { document: String, name: String },
|
SaveDocument { document: String, name: String },
|
||||||
|
|
@ -50,6 +51,7 @@ impl MessageHandler<FrontendMessage, ()> for FrontendMessageHandler {
|
||||||
ExpandFolder,
|
ExpandFolder,
|
||||||
SetActiveTool,
|
SetActiveTool,
|
||||||
UpdateCanvas,
|
UpdateCanvas,
|
||||||
|
UpdateScrollbars,
|
||||||
EnableTextInput,
|
EnableTextInput,
|
||||||
DisableTextInput,
|
DisableTextInput,
|
||||||
SetCanvasZoom,
|
SetCanvasZoom,
|
||||||
|
|
|
||||||
|
|
@ -123,11 +123,23 @@
|
||||||
</div>
|
</div>
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
<LayoutCol :class="'bar-area'">
|
<LayoutCol :class="'bar-area'">
|
||||||
<PersistentScrollbar :direction="ScrollbarDirection.Vertical" :class="'right-scrollbar'" />
|
<PersistentScrollbar
|
||||||
|
:direction="ScrollbarDirection.Vertical"
|
||||||
|
:handlePosition="scrollbarPos.y"
|
||||||
|
@update:handlePosition="translateCanvasY"
|
||||||
|
v-model:handleLength="scrollbarSize.y"
|
||||||
|
:class="'right-scrollbar'"
|
||||||
|
/>
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<LayoutRow :class="'bar-area'">
|
<LayoutRow :class="'bar-area'">
|
||||||
<PersistentScrollbar :direction="ScrollbarDirection.Horizontal" :class="'bottom-scrollbar'" />
|
<PersistentScrollbar
|
||||||
|
:direction="ScrollbarDirection.Horizontal"
|
||||||
|
:handlePosition="scrollbarPos.x"
|
||||||
|
@update:handlePosition="translateCanvasX"
|
||||||
|
v-model:handleLength="scrollbarSize.x"
|
||||||
|
:class="'bottom-scrollbar'"
|
||||||
|
/>
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
|
|
@ -210,7 +222,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
import { ResponseType, registerResponseHandler, Response, UpdateCanvas, SetActiveTool, SetCanvasZoom, SetCanvasRotation } from "@/utilities/response-handler";
|
import { ResponseType, registerResponseHandler, Response, UpdateCanvas, UpdateScrollbars, SetActiveTool, SetCanvasZoom, SetCanvasRotation } from "@/utilities/response-handler";
|
||||||
import { SeparatorDirection, SeparatorType } from "@/components/widgets/widgets";
|
import { SeparatorDirection, SeparatorType } from "@/components/widgets/widgets";
|
||||||
import { comingSoon } from "@/utilities/errors";
|
import { comingSoon } from "@/utilities/errors";
|
||||||
|
|
||||||
|
|
@ -271,6 +283,16 @@ export default defineComponent({
|
||||||
async setRotation(newRotation: number) {
|
async setRotation(newRotation: number) {
|
||||||
(await wasm).set_rotation(newRotation * (Math.PI / 180));
|
(await wasm).set_rotation(newRotation * (Math.PI / 180));
|
||||||
},
|
},
|
||||||
|
async translateCanvasX(newValue: number) {
|
||||||
|
const delta = newValue - this.scrollbarPos.x;
|
||||||
|
this.scrollbarPos.x = newValue;
|
||||||
|
(await wasm).translate_canvas(-delta * this.scrollbarMultiplier.x, 0);
|
||||||
|
},
|
||||||
|
async translateCanvasY(newValue: number) {
|
||||||
|
const delta = newValue - this.scrollbarPos.y;
|
||||||
|
this.scrollbarPos.y = newValue;
|
||||||
|
(await wasm).translate_canvas(0, -delta * this.scrollbarMultiplier.y);
|
||||||
|
},
|
||||||
async selectTool(toolName: string) {
|
async selectTool(toolName: string) {
|
||||||
(await wasm).select_tool(toolName);
|
(await wasm).select_tool(toolName);
|
||||||
},
|
},
|
||||||
|
|
@ -287,6 +309,15 @@ export default defineComponent({
|
||||||
if (updateData) this.viewportSvg = updateData.document;
|
if (updateData) this.viewportSvg = updateData.document;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
registerResponseHandler(ResponseType.UpdateScrollbars, (responseData: Response) => {
|
||||||
|
const updateData = responseData as UpdateScrollbars;
|
||||||
|
if (updateData) {
|
||||||
|
this.scrollbarPos = updateData.position;
|
||||||
|
this.scrollbarSize = updateData.size;
|
||||||
|
this.scrollbarMultiplier = updateData.multiplier;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
registerResponseHandler(ResponseType.SetActiveTool, (responseData: Response) => {
|
registerResponseHandler(ResponseType.SetActiveTool, (responseData: Response) => {
|
||||||
const toolData = responseData as SetActiveTool;
|
const toolData = responseData as SetActiveTool;
|
||||||
if (toolData) this.activeTool = toolData.tool_name;
|
if (toolData) this.activeTool = toolData.tool_name;
|
||||||
|
|
@ -325,6 +356,9 @@ export default defineComponent({
|
||||||
overlaysEnabled: true,
|
overlaysEnabled: true,
|
||||||
documentRotation: 0,
|
documentRotation: 0,
|
||||||
documentZoom: 100,
|
documentZoom: 100,
|
||||||
|
scrollbarPos: { x: 0.5, y: 0.5 },
|
||||||
|
scrollbarSize: { x: 0.5, y: 0.5 },
|
||||||
|
scrollbarMultiplier: { x: 0, y: 0 },
|
||||||
IncrementBehavior,
|
IncrementBehavior,
|
||||||
IncrementDirection,
|
IncrementDirection,
|
||||||
MenuDirection,
|
MenuDirection,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="persistent-scrollbar" :class="direction.toLowerCase()">
|
<div class="persistent-scrollbar" :class="direction.toLowerCase()">
|
||||||
<button class="arrow decrease"></button>
|
<button class="arrow decrease" @mousedown="changePosition(-50)"></button>
|
||||||
<div class="scroll-track">
|
<div class="scroll-track" ref="scrollTrack" @mousedown="grabArea">
|
||||||
<div class="scroll-click-area decrease" :style="[trackStart, preThumb, sides]"></div>
|
<div class="scroll-thumb" @mousedown="grabHandle" :class="{ dragging }" ref="handle" :style="[thumbStart, thumbEnd, sides]"></div>
|
||||||
<div class="scroll-thumb" :style="[thumbStart, thumbEnd, sides]"></div>
|
|
||||||
<div class="scroll-click-area increase" :style="[postThumb, trackEnd, sides]"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<button class="arrow increase"></button>
|
<button class="arrow increase" @click="changePosition(50)"></button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -39,6 +37,9 @@
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--color-6-lowergray);
|
background: var(--color-6-lowergray);
|
||||||
}
|
}
|
||||||
|
&.dragging {
|
||||||
|
background: var(--color-accent-hover);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-click-area {
|
.scroll-click-area {
|
||||||
|
|
@ -57,6 +58,9 @@
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: transparent transparent var(--color-6-lowergray) transparent;
|
border-color: transparent transparent var(--color-6-lowergray) transparent;
|
||||||
}
|
}
|
||||||
|
&:active {
|
||||||
|
border-color: transparent transparent var(--color-c-brightgray) transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.arrow.increase {
|
.arrow.increase {
|
||||||
|
|
@ -67,6 +71,9 @@
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: var(--color-6-lowergray) transparent transparent transparent;
|
border-color: var(--color-6-lowergray) transparent transparent transparent;
|
||||||
}
|
}
|
||||||
|
&:active {
|
||||||
|
border-color: var(--color-c-brightgray) transparent transparent transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,6 +88,9 @@
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: transparent var(--color-6-lowergray) transparent transparent;
|
border-color: transparent var(--color-6-lowergray) transparent transparent;
|
||||||
}
|
}
|
||||||
|
&:active {
|
||||||
|
border-color: transparent var(--color-c-brightgray) transparent transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.arrow.increase {
|
.arrow.increase {
|
||||||
|
|
@ -91,6 +101,9 @@
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: transparent transparent transparent var(--color-6-lowergray);
|
border-color: transparent transparent transparent var(--color-6-lowergray);
|
||||||
}
|
}
|
||||||
|
&:active {
|
||||||
|
border-color: transparent transparent transparent var(--color-c-brightgray);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -99,6 +112,15 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, PropType } from "vue";
|
import { defineComponent, PropType } from "vue";
|
||||||
|
|
||||||
|
// Linear Interpolation
|
||||||
|
const lerp = (x: number, y: number, a: number) => x * (1 - a) + y * a;
|
||||||
|
|
||||||
|
// Convert the position of the handle (0-1) to the position on the track (0-1).
|
||||||
|
// This includes the 1/2 handle length gap of the possible handle positionson each side so the end of the handle doesn't go off the track.
|
||||||
|
const handleToTrack = (handleLen: number, handlePos: number) => lerp(handleLen / 2, 1 - handleLen / 2, handlePos);
|
||||||
|
|
||||||
|
const mousePosition = (direction: ScrollbarDirection, e: MouseEvent) => (direction === ScrollbarDirection.Vertical ? e.clientY : e.clientX);
|
||||||
|
|
||||||
export enum ScrollbarDirection {
|
export enum ScrollbarDirection {
|
||||||
"Horizontal" = "Horizontal",
|
"Horizontal" = "Horizontal",
|
||||||
"Vertical" = "Vertical",
|
"Vertical" = "Vertical",
|
||||||
|
|
@ -107,33 +129,19 @@ export enum ScrollbarDirection {
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
direction: { type: String as PropType<ScrollbarDirection>, default: ScrollbarDirection.Vertical },
|
direction: { type: String as PropType<ScrollbarDirection>, default: ScrollbarDirection.Vertical },
|
||||||
|
handlePosition: { type: Number, default: 0.5 },
|
||||||
|
handleLength: { type: Number, default: 0.5 },
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
trackStart(): { left: string } | { top: string } {
|
|
||||||
return this.direction === ScrollbarDirection.Vertical ? { top: "0%" } : { left: "0%" };
|
|
||||||
},
|
|
||||||
preThumb(): { right: string } | { bottom: string } {
|
|
||||||
const start = 25;
|
|
||||||
|
|
||||||
return this.direction === ScrollbarDirection.Vertical ? { bottom: `${100 - start}%` } : { right: `${100 - start}%` };
|
|
||||||
},
|
|
||||||
thumbStart(): { left: string } | { top: string } {
|
thumbStart(): { left: string } | { top: string } {
|
||||||
const start = 25;
|
const start = handleToTrack(this.handleLength, this.handlePosition) - this.handleLength / 2;
|
||||||
|
|
||||||
return this.direction === ScrollbarDirection.Vertical ? { top: `${start}%` } : { left: `${start}%` };
|
return this.direction === ScrollbarDirection.Vertical ? { top: `${start * 100}%` } : { left: `${start * 100}%` };
|
||||||
},
|
},
|
||||||
thumbEnd(): { right: string } | { bottom: string } {
|
thumbEnd(): { right: string } | { bottom: string } {
|
||||||
const end = 25;
|
const end = 1 - handleToTrack(this.handleLength, this.handlePosition) - this.handleLength / 2;
|
||||||
|
|
||||||
return this.direction === ScrollbarDirection.Vertical ? { bottom: `${end}%` } : { right: `${end}%` };
|
return this.direction === ScrollbarDirection.Vertical ? { bottom: `${end * 100}%` } : { right: `${end * 100}%` };
|
||||||
},
|
|
||||||
postThumb(): { left: string } | { top: string } {
|
|
||||||
const end = 25;
|
|
||||||
|
|
||||||
return this.direction === ScrollbarDirection.Vertical ? { top: `${100 - end}%` } : { left: `${100 - end}%` };
|
|
||||||
},
|
|
||||||
trackEnd(): { right: string } | { bottom: string } {
|
|
||||||
return this.direction === ScrollbarDirection.Vertical ? { bottom: "0%" } : { right: "0%" };
|
|
||||||
},
|
},
|
||||||
sides(): { left: string; right: string } | { top: string; bottom: string } {
|
sides(): { left: string; right: string } | { top: string; bottom: string } {
|
||||||
return this.direction === ScrollbarDirection.Vertical ? { left: "0%", right: "0%" } : { top: "0%", bottom: "0%" };
|
return this.direction === ScrollbarDirection.Vertical ? { left: "0%", right: "0%" } : { top: "0%", bottom: "0%" };
|
||||||
|
|
@ -142,7 +150,58 @@ export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
ScrollbarDirection,
|
ScrollbarDirection,
|
||||||
|
dragging: false,
|
||||||
|
mousePos: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
window.addEventListener("mouseup", () => {
|
||||||
|
this.dragging = false;
|
||||||
|
});
|
||||||
|
window.addEventListener("mousemove", this.mouseMove);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
trackLength(): number {
|
||||||
|
const track = this.$refs.scrollTrack as HTMLElement;
|
||||||
|
return this.direction === ScrollbarDirection.Vertical ? track.clientHeight - this.handleLength : track.clientWidth;
|
||||||
|
},
|
||||||
|
trackOffset(): number {
|
||||||
|
const track = this.$refs.scrollTrack as HTMLElement;
|
||||||
|
return this.direction === ScrollbarDirection.Vertical ? track.getBoundingClientRect().top : track.getBoundingClientRect().left;
|
||||||
|
},
|
||||||
|
clampHandlePosition(newPos: number) {
|
||||||
|
const clampedPosition = Math.min(Math.max(newPos, 0), 1);
|
||||||
|
this.$emit("update:handlePosition", clampedPosition);
|
||||||
|
},
|
||||||
|
updateHandlePosition(e: MouseEvent) {
|
||||||
|
const position = mousePosition(this.direction, e);
|
||||||
|
this.clampHandlePosition(this.handlePosition + (position - this.mousePos) / (this.trackLength() * (1 - this.handleLength)));
|
||||||
|
this.mousePos = position;
|
||||||
|
},
|
||||||
|
grabHandle(e: MouseEvent) {
|
||||||
|
if (!this.dragging) {
|
||||||
|
this.dragging = true;
|
||||||
|
this.mousePos = mousePosition(this.direction, e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grabArea(e: MouseEvent) {
|
||||||
|
if (!this.dragging) {
|
||||||
|
this.dragging = true;
|
||||||
|
this.mousePos = mousePosition(this.direction, e);
|
||||||
|
this.clampHandlePosition(((this.mousePos - this.trackOffset()) / this.trackLength() - this.handleLength / 2) / (1 - this.handleLength));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mouseUp() {
|
||||||
|
this.dragging = false;
|
||||||
|
},
|
||||||
|
mouseMove(e: MouseEvent) {
|
||||||
|
if (this.dragging) {
|
||||||
|
this.updateHandlePosition(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
changePosition(difference: number) {
|
||||||
|
this.clampHandlePosition(this.handlePosition + difference / this.trackLength());
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ const state = reactive({
|
||||||
|
|
||||||
export enum ResponseType {
|
export enum ResponseType {
|
||||||
UpdateCanvas = "UpdateCanvas",
|
UpdateCanvas = "UpdateCanvas",
|
||||||
|
UpdateScrollbars = "UpdateScrollbars",
|
||||||
ExportDocument = "ExportDocument",
|
ExportDocument = "ExportDocument",
|
||||||
SaveDocument = "SaveDocument",
|
SaveDocument = "SaveDocument",
|
||||||
OpenDocumentBrowse = "OpenDocumentBrowse",
|
OpenDocumentBrowse = "OpenDocumentBrowse",
|
||||||
|
|
@ -66,6 +67,8 @@ function parseResponse(responseType: string, data: any): Response {
|
||||||
return newUpdateOpenDocumentsList(data.UpdateOpenDocumentsList);
|
return newUpdateOpenDocumentsList(data.UpdateOpenDocumentsList);
|
||||||
case "UpdateCanvas":
|
case "UpdateCanvas":
|
||||||
return newUpdateCanvas(data.UpdateCanvas);
|
return newUpdateCanvas(data.UpdateCanvas);
|
||||||
|
case "UpdateScrollbars":
|
||||||
|
return newUpdateScrollbars(data.UpdateScrollbars);
|
||||||
case "UpdateLayer":
|
case "UpdateLayer":
|
||||||
return newUpdateLayer(data.UpdateLayer);
|
return newUpdateLayer(data.UpdateLayer);
|
||||||
case "SetCanvasZoom":
|
case "SetCanvasZoom":
|
||||||
|
|
@ -91,7 +94,7 @@ function parseResponse(responseType: string, data: any): Response {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Response = SetActiveTool | UpdateCanvas | DocumentChanged | CollapseFolder | ExpandFolder | UpdateWorkingColors | SetCanvasZoom | SetCanvasRotation;
|
export type Response = SetActiveTool | UpdateCanvas | UpdateScrollbars | DocumentChanged | CollapseFolder | ExpandFolder | UpdateWorkingColors | SetCanvasZoom | SetCanvasRotation;
|
||||||
|
|
||||||
export interface UpdateOpenDocumentsList {
|
export interface UpdateOpenDocumentsList {
|
||||||
open_documents: Array<string>;
|
open_documents: Array<string>;
|
||||||
|
|
@ -171,6 +174,19 @@ function newUpdateCanvas(input: any): UpdateCanvas {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UpdateScrollbars {
|
||||||
|
position: { x: number; y: number };
|
||||||
|
size: { x: number; y: number };
|
||||||
|
multiplier: { x: number; y: number };
|
||||||
|
}
|
||||||
|
function newUpdateScrollbars(input: any): UpdateScrollbars {
|
||||||
|
return {
|
||||||
|
position: { x: input.position[0], y: input.position[1] },
|
||||||
|
size: { x: input.size[0], y: input.size[1] },
|
||||||
|
multiplier: { x: input.multiplier[0], y: input.multiplier[1] },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface ExportDocument {
|
export interface ExportDocument {
|
||||||
document: string;
|
document: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
||||||
|
|
@ -305,6 +305,13 @@ pub fn set_rotation(new_radians: f64) -> Result<(), JsValue> {
|
||||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
|
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Translates document (in viewport coords)
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn translate_canvas(delta_x: f64, delta_y: f64) -> Result<(), JsValue> {
|
||||||
|
let ev = MovementMessage::TranslateCanvas((delta_x, delta_y).into());
|
||||||
|
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
|
||||||
|
}
|
||||||
|
|
||||||
/// Update the list of selected layers. The layer paths have to be stored in one array and are separated by LayerId::MAX
|
/// Update the list of selected layers. The layer paths have to be stored in one array and are separated by LayerId::MAX
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn select_layers(paths: Vec<LayerId>) -> Result<(), JsValue> {
|
pub fn select_layers(paths: Vec<LayerId>) -> Result<(), JsValue> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue