Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make scrollbars interactable #328

Merged
merged 30 commits into from
Aug 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3bf0aa6
Make scrollbars interactable
0HyperCube Aug 2, 2021
6891f09
Add watcher for position change
0HyperCube Aug 2, 2021
b1d728d
Fix case of data
0HyperCube Aug 2, 2021
692917d
Merge branch 'master' into document-scrollbars
0HyperCube Aug 7, 2021
4457bb1
Fix updateHandlePosition capitalization
0HyperCube Aug 7, 2021
6b19620
Clean up class name thing
0HyperCube Aug 7, 2021
dbde806
Scroll bars between 0 and 1
0HyperCube Aug 7, 2021
f080821
Allow width to be 100%
0HyperCube Aug 7, 2021
6261d9f
Scrollbars reflect backend
0HyperCube Aug 7, 2021
f8d2c54
Merge branch 'master' into document-scrollbars
0HyperCube Aug 7, 2021
b38f9bc
Include viewport in scrollbar
0HyperCube Aug 7, 2021
c4163bd
Merge branch 'master' into document-scrollbars
0HyperCube Aug 9, 2021
49b72c5
Add half viewport padding for scrollbars
0HyperCube Aug 9, 2021
85141c0
Refactor scrollbar using lerp
0HyperCube Aug 9, 2021
6f5b846
Send messages to backend
0HyperCube Aug 10, 2021
4d93a69
Merge branch 'master' into document-scrollbars
0HyperCube Aug 10, 2021
d886dae
Refactor
0HyperCube Aug 11, 2021
eb58cc5
Use glam::DVec2
0HyperCube Aug 11, 2021
90cb0c4
Remove glam::
0HyperCube Aug 11, 2021
4265f23
Merge branch 'master' into document-scrollbars
0HyperCube Aug 11, 2021
fbcf216
Remove unnecessary abs
0HyperCube Aug 11, 2021
f01627a
Merge branch 'document-scrollbars' of https://github.com/GraphiteEdit…
0HyperCube Aug 11, 2021
bd13481
Add TrueDoctor's change
0HyperCube Aug 11, 2021
e751252
Add missing minus
0HyperCube Aug 11, 2021
dcb7b0a
Merge branch 'master' into document-scrollbars
0HyperCube Aug 14, 2021
5bbf3db
Fix vue issues
0HyperCube Aug 15, 2021
cbe4e74
Merge branch 'master' into document-scrollbars
0HyperCube Aug 15, 2021
d46e67a
Fix viewport size
0HyperCube Aug 15, 2021
6eb6d39
Remove unnecessary log
0HyperCube Aug 15, 2021
e1f46f4
Linear dragging
0HyperCube Aug 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions editor/src/communication/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ impl Dispatcher {
| Message::InputMapper(_)
| Message::Documents(DocumentsMessage::Document(DocumentMessage::RenderDocument))
| Message::Frontend(FrontendMessage::UpdateCanvas { .. })
| Message::Frontend(FrontendMessage::UpdateScrollbars { .. })
| Message::Frontend(FrontendMessage::SetCanvasZoom { .. })
| Message::Frontend(FrontendMessage::SetCanvasRotation { .. })
| Message::Documents(DocumentsMessage::Document(DocumentMessage::DispatchOperation { .. }))
Expand Down
42 changes: 27 additions & 15 deletions editor/src/document/document_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,6 @@ impl From<DocumentOperation> for Message {
}

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 {
let len = document_responses.len();
document_responses.retain(|response| !matches!(response, DocumentResponse::DocumentChanged));
Expand Down Expand Up @@ -320,9 +314,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
)
}
SetBlendModeForSelectedLayers(blend_mode) => {
let active_document = self;

for path in active_document.layer_data.iter().filter_map(|(path, data)| data.selected.then(|| path.clone())) {
for path in self.layer_data.iter().filter_map(|(path, data)| data.selected.then(|| path.clone())) {
responses.push_back(DocumentOperation::SetLayerBlendMode { path, blend_mode }.into());
}
}
Expand Down Expand Up @@ -407,12 +399,32 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
Err(e) => log::error!("DocumentError: {:?}", e),
Ok(_) => (),
},
RenderDocument => responses.push_back(
FrontendMessage::UpdateCanvas {
document: self.document.render_root(),
}
.into(),
),
RenderDocument => {
responses.push_back(
FrontendMessage::UpdateCanvas {
document: self.document.render_root(),
}
.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) => {
for path in self.selected_layers().cloned() {
let operation = DocumentOperation::TransformLayerInViewport {
Expand Down
7 changes: 7 additions & 0 deletions editor/src/document/movement_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub enum MovementMessage {
DecreaseCanvasZoom,
WheelCanvasZoom,
ZoomCanvasToFitAll,
TranslateCanvas(glam::DVec2),
}

#[derive(Debug, Clone, Default, PartialEq)]
Expand Down Expand Up @@ -189,6 +190,12 @@ impl MessageHandler<MovementMessage, (&mut LayerData, &Document, &InputPreproces
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 {
Expand Down
2 changes: 2 additions & 0 deletions editor/src/frontend/frontend_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub enum FrontendMessage {
DisplayConfirmationToCloseDocument { document_index: usize },
DisplayConfirmationToCloseAllDocuments,
UpdateCanvas { document: String },
UpdateScrollbars { position: (f64, f64), size: (f64, f64), multiplier: (f64, f64) },
UpdateLayer { path: Vec<LayerId>, data: LayerPanelEntry },
ExportDocument { document: String, name: String },
SaveDocument { document: String, name: String },
Expand Down Expand Up @@ -50,6 +51,7 @@ impl MessageHandler<FrontendMessage, ()> for FrontendMessageHandler {
ExpandFolder,
SetActiveTool,
UpdateCanvas,
UpdateScrollbars,
EnableTextInput,
DisableTextInput,
SetCanvasZoom,
Expand Down
40 changes: 37 additions & 3 deletions frontend/src/components/panels/Document.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,23 @@
</div>
</LayoutCol>
<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>
</LayoutRow>
<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>
</LayoutCol>
</LayoutRow>
Expand Down Expand Up @@ -210,7 +222,7 @@
<script lang="ts">
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 { comingSoon } from "@/utilities/errors";

Expand Down Expand Up @@ -271,6 +283,16 @@ export default defineComponent({
async setRotation(newRotation: number) {
(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) {
(await wasm).select_tool(toolName);
},
Expand All @@ -287,6 +309,15 @@ export default defineComponent({
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) => {
const toolData = responseData as SetActiveTool;
if (toolData) this.activeTool = toolData.tool_name;
Expand Down Expand Up @@ -325,6 +356,9 @@ export default defineComponent({
overlaysEnabled: true,
documentRotation: 0,
documentZoom: 100,
scrollbarPos: { x: 0.5, y: 0.5 },
scrollbarSize: { x: 0.5, y: 0.5 },
scrollbarMultiplier: { x: 0, y: 0 },
IncrementBehavior,
IncrementDirection,
MenuDirection,
Expand Down
111 changes: 85 additions & 26 deletions frontend/src/components/widgets/scrollbars/PersistentScrollbar.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
<template>
<div class="persistent-scrollbar" :class="direction.toLowerCase()">
<button class="arrow decrease"></button>
<div class="scroll-track">
<div class="scroll-click-area decrease" :style="[trackStart, preThumb, sides]"></div>
<div class="scroll-thumb" :style="[thumbStart, thumbEnd, sides]"></div>
<div class="scroll-click-area increase" :style="[postThumb, trackEnd, sides]"></div>
<button class="arrow decrease" @mousedown="changePosition(-50)"></button>
<div class="scroll-track" ref="scrollTrack" @mousedown="grabArea">
<div class="scroll-thumb" @mousedown="grabHandle" :class="{ dragging }" ref="handle" :style="[thumbStart, thumbEnd, sides]"></div>
</div>
<button class="arrow increase"></button>
<button class="arrow increase" @click="changePosition(50)"></button>
</div>
</template>

Expand Down Expand Up @@ -39,6 +37,9 @@
&:hover {
background: var(--color-6-lowergray);
}
&.dragging {
background: var(--color-accent-hover);
}
}

.scroll-click-area {
Expand All @@ -57,6 +58,9 @@
&:hover {
border-color: transparent transparent var(--color-6-lowergray) transparent;
}
&:active {
border-color: transparent transparent var(--color-c-brightgray) transparent;
}
}

.arrow.increase {
Expand All @@ -67,6 +71,9 @@
&:hover {
border-color: var(--color-6-lowergray) transparent transparent transparent;
}
&:active {
border-color: var(--color-c-brightgray) transparent transparent transparent;
}
}
}

Expand All @@ -81,6 +88,9 @@
&:hover {
border-color: transparent var(--color-6-lowergray) transparent transparent;
}
&:active {
border-color: transparent var(--color-c-brightgray) transparent transparent;
}
}

.arrow.increase {
Expand All @@ -91,6 +101,9 @@
&:hover {
border-color: transparent transparent transparent var(--color-6-lowergray);
}
&:active {
border-color: transparent transparent transparent var(--color-c-brightgray);
}
}
}
}
Expand All @@ -99,6 +112,15 @@
<script lang="ts">
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 {
"Horizontal" = "Horizontal",
"Vertical" = "Vertical",
Expand All @@ -107,33 +129,19 @@ export enum ScrollbarDirection {
export default defineComponent({
props: {
direction: { type: String as PropType<ScrollbarDirection>, default: ScrollbarDirection.Vertical },
handlePosition: { type: Number, default: 0.5 },
handleLength: { type: Number, default: 0.5 },
},
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 } {
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 } {
const end = 25;
const end = 1 - handleToTrack(this.handleLength, this.handlePosition) - this.handleLength / 2;

return this.direction === ScrollbarDirection.Vertical ? { bottom: `${end}%` } : { right: `${end}%` };
},
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%" };
return this.direction === ScrollbarDirection.Vertical ? { bottom: `${end * 100}%` } : { right: `${end * 100}%` };
},
sides(): { left: string; right: string } | { top: string; bottom: string } {
return this.direction === ScrollbarDirection.Vertical ? { left: "0%", right: "0%" } : { top: "0%", bottom: "0%" };
Expand All @@ -142,7 +150,58 @@ export default defineComponent({
data() {
return {
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>
Loading