Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
185 changes: 125 additions & 60 deletions Cargo.lock

Large diffs are not rendered by default.

16 changes: 10 additions & 6 deletions editor/src/document/document_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,13 @@ impl DocumentMessageHandler {
let shapes = self.selected_layers().filter_map(|path_to_shape| {
let viewport_transform = self.graphene_document.generate_transform_relative_to_viewport(path_to_shape).ok()?;

let shape = match &self.graphene_document.layer(path_to_shape).ok()?.data {
LayerDataType::Shape(shape) => Some(shape),
let path = match &self.graphene_document.layer(path_to_shape).ok()?.data {
LayerDataType::Shape(shape) => Some(shape.path.clone()),
LayerDataType::Folder(_) => None,
LayerDataType::Text(text) => Some(text.bezpath.clone()),
}?;
let path = shape.path.clone();

log::info!("Bez {:?}", path);

let segments = path
.segments()
Expand Down Expand Up @@ -210,6 +212,7 @@ impl DocumentMessageHandler {
space += 1;
match layer.data {
LayerDataType::Shape(_) => (),
LayerDataType::Text(_) => (),
LayerDataType::Folder(ref folder) => {
path.push(*id);
if self.layerdata(path).expanded {
Expand Down Expand Up @@ -443,7 +446,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
size.x,
size.y,
"\n",
self.graphene_document.render_root()
self.graphene_document.render_root(false)
),
name,
}
Expand Down Expand Up @@ -577,7 +580,8 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
responses.push_back(FolderChanged(vec![]).into());
}
FolderChanged(path) => {
let _ = self.graphene_document.render_root();
// ToDo: text editable only enabled if in text tool
let _ = self.graphene_document.render_root(true);
responses.extend([LayerChanged(path).into(), DocumentStructureChanged.into()]);
}
DocumentStructureChanged => {
Expand Down Expand Up @@ -619,7 +623,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
RenderDocument => {
responses.push_back(
FrontendMessage::UpdateCanvas {
document: self.graphene_document.render_root(),
document: self.graphene_document.render_root(true), // ToDo: text editable only enabled if in text tool
}
.into(),
);
Expand Down
8 changes: 6 additions & 2 deletions editor/src/document/layer_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@ pub fn layer_data<'a>(layer_data: &'a mut HashMap<Vec<LayerId>, LayerData>, path
layer_data.get_mut(path).expect(&format!("Layer data cannot be found because the path {:?} does not exist", path))
}

pub fn layer_panel_entry(layer_data: &LayerData, transform: DAffine2, layer: &Layer, path: Vec<LayerId>) -> LayerPanelEntry {
pub fn layer_panel_entry(layer_data: &LayerData, transform: DAffine2, layer: &Layer, mut path: Vec<LayerId>) -> LayerPanelEntry {
let layer_type: LayerType = (&layer.data).into();
let name = layer.name.clone().unwrap_or_else(|| format!("Unnamed {}", layer_type));
let arr = layer.data.bounding_box(transform).unwrap_or([DVec2::ZERO, DVec2::ZERO]);
let arr = arr.iter().map(|x| (*x).into()).collect::<Vec<(f64, f64)>>();

let mut thumbnail = String::new();
layer.data.clone().render(&mut thumbnail, &mut vec![transform]);
layer.data.clone().render(&mut thumbnail, &mut vec![transform], &mut path, false);
let transform = transform.to_cols_array().iter().map(ToString::to_string).collect::<Vec<_>>().join(",");
let thumbnail = if let [(x_min, y_min), (x_max, y_max)] = arr.as_slice() {
format!(
Expand Down Expand Up @@ -100,6 +100,7 @@ impl From<Vec<LayerId>> for Path {
Self(iter)
}
}

impl Serialize for Path {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
Expand Down Expand Up @@ -163,13 +164,15 @@ pub struct LayerPanelEntry {
pub enum LayerType {
Folder,
Shape,
Text,
}

impl fmt::Display for LayerType {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let name = match self {
LayerType::Folder => "Folder",
LayerType::Shape => "Shape",
LayerType::Text => "Text",
};

formatter.write_str(name)
Expand All @@ -182,6 +185,7 @@ impl From<&LayerDataType> for LayerType {
match data {
Folder(_) => LayerType::Folder,
Shape(_) => LayerType::Shape,
Text(_) => LayerType::Text,
}
}
}
3 changes: 3 additions & 0 deletions editor/src/input/input_mapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ impl Default for Mapping {
entry! {action=SelectMessage::DragStop, key_up=Lmb},
entry! {action=SelectMessage::Abort, key_down=Rmb},
entry! {action=SelectMessage::Abort, key_down=KeyEscape},
// Text
entry! {action=TextMessage::PlaceText, key_down=Lmb},
// Eyedropper
entry! {action=EyedropperMessage::LeftMouseDown, key_down=Lmb},
entry! {action=EyedropperMessage::RightMouseDown, key_down=Rmb},
Expand Down Expand Up @@ -191,6 +193,7 @@ impl Default for Mapping {
// Tool Actions
entry! {action=ToolMessage::ActivateTool(ToolType::Select), key_down=KeyV},
entry! {action=ToolMessage::ActivateTool(ToolType::Eyedropper), key_down=KeyI},
entry! {action=ToolMessage::ActivateTool(ToolType::Text), key_down=KeyT},
entry! {action=ToolMessage::ActivateTool(ToolType::Fill), key_down=KeyF},
entry! {action=ToolMessage::ActivateTool(ToolType::Path), key_down=KeyA},
entry! {action=ToolMessage::ActivateTool(ToolType::Pen), key_down=KeyP},
Expand Down
1 change: 1 addition & 0 deletions editor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ pub mod message_prelude {
pub use crate::tool::tools::rectangle::{RectangleMessage, RectangleMessageDiscriminant};
pub use crate::tool::tools::select::{SelectMessage, SelectMessageDiscriminant};
pub use crate::tool::tools::shape::{ShapeMessage, ShapeMessageDiscriminant};
pub use crate::tool::tools::text::{TextMessage, TextMessageDiscriminant};
pub use crate::LayerId;
pub use graphite_proc_macros::*;
pub use std::collections::VecDeque;
Expand Down
1 change: 1 addition & 0 deletions editor/src/tool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ impl Default for ToolFsmState {
Line => line::Line,
Shape => shape::Shape,
Ellipse => ellipse::Ellipse,
Text => text::Text,
Fill => fill::Fill,
},
},
Expand Down
5 changes: 5 additions & 0 deletions editor/src/tool/tool_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use crate::{
use serde::{Deserialize, Serialize};
use std::collections::VecDeque;

use super::tools::text::TextMessage;

#[impl_message(Message, Tool)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
pub enum ToolMessage {
Expand All @@ -21,6 +23,8 @@ pub enum ToolMessage {
NoOp,
SetToolOptions(ToolType, ToolOptions),
#[child]
Text(TextMessage),
#[child]
Fill(FillMessage),
#[child]
Rectangle(RectangleMessage),
Expand Down Expand Up @@ -167,6 +171,7 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, &InputPreprocessor)>
fn message_to_tool_type(message: &ToolMessage) -> ToolType {
use ToolMessage::*;
let tool_type = match message {
Text(_) => ToolType::Text,
Fill(_) => ToolType::Fill,
Rectangle(_) => ToolType::Rectangle,
Ellipse(_) => ToolType::Ellipse,
Expand Down
1 change: 1 addition & 0 deletions editor/src/tool/tools/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod pen;
pub mod rectangle;
pub mod resize;
pub mod shape;
pub mod text;

// not implemented yet
pub mod crop;
Expand Down
48 changes: 48 additions & 0 deletions editor/src/tool/tools/text.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use crate::message_prelude::*;
use crate::tool::ToolActionHandlerData;
use glam::{DAffine2, UVec2};
use graphene::{layers::style, Operation};
use serde::{Deserialize, Serialize};

#[derive(Default)]
pub struct Text;

#[impl_message(Message, ToolMessage, Text)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
pub enum TextMessage {
PlaceText,
InputChanged { path: String, value: String, size: [f64; 2] },
}

impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Text {
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
if let ToolMessage::Text(action) = action {
match action {
TextMessage::PlaceText => {
let path = vec![generate_uuid()];
responses.extend([
Operation::AddText {
path: path.clone(),
insert_index: -1,
style: style::PathStyle::new(None, Some(style::Fill::new(data.1.primary_color))),
}
.into(),
Operation::SetLayerTransformInViewport {
path,
transform: DAffine2::from_translation(data.2.mouse.position).to_cols_array(),
}
.into(),
]);
}
TextMessage::InputChanged { path, value, size } => {
let path = path.split(",").map(|x| x.parse::<LayerId>().unwrap()).collect::<Vec<LayerId>>();
log::info!("Path {:?} to value {}", path, value);
responses.push_back(Operation::SetText { path: path, text: value, size }.into());
}
}
}
}
fn actions(&self) -> ActionList {
actions!(TextMessageDiscriminant; PlaceText)
}
}
7 changes: 7 additions & 0 deletions frontend/assets/24px-full-color/node-type-text.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 17 additions & 1 deletion frontend/src/components/panels/Document.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@

<Separator :type="SeparatorType.Section" :direction="SeparatorDirection.Vertical" />

<ShelfItemInput icon="ParametricTextTool" title="Text Tool (T)" :active="activeTool === 'Text'" :action="() => comingSoon(153) && selectTool('Text')" />
<ShelfItemInput icon="ParametricTextTool" title="Text Tool (T)" :active="activeTool === 'Text'" :action="() => selectTool('Text')" />
<ShelfItemInput icon="ParametricFillTool" title="Fill Tool (F)" :active="activeTool === 'Fill'" :action="() => selectTool('Fill')" />
<ShelfItemInput icon="ParametricGradientTool" title="Gradient Tool (H)" :active="activeTool === 'Gradient'" :action="() => comingSoon() && selectTool('Gradient')" />

Expand Down Expand Up @@ -214,6 +214,22 @@
// Fallback values if JS hasn't set these to integers yet
width: 100%;
height: 100%;

// Style editable text boxes
textarea {
color: black;
border: none;
outline: none;
background: none;
padding: 0;
margin-top: 0px;

-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;

resize: none; /*remove the resize handle on the bottom right*/
}
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/panels/LayerTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
<div class="layer-thumbnail" v-html="layer.thumbnail"></div>
<div class="layer-type-icon">
<IconLabel v-if="layer.layer_type === LayerType.Folder" :icon="'NodeTypeFolder'" title="Folder" />
<IconLabel v-else :icon="'NodeTypePath'" title="Path" />
<IconLabel v-if="layer.layer_type === LayerType.Shape" :icon="'NodeTypePath'" title="Path" />
<IconLabel v-if="layer.layer_type === LayerType.Text" :icon="'NodeTypeText'" title="Text" />
</div>
<div class="layer-name">
<span>{{ layer.name }}</span>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/widgets/labels/IconLabel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ import MouseHintMMBDrag from "@/../assets/16px-two-tone/mouse-hint-mmb-drag.svg"

import NodeTypePath from "@/../assets/24px-full-color/node-type-path.svg";
import NodeTypeFolder from "@/../assets/24px-full-color/node-type-folder.svg";
import NodeTypeText from "@/../assets/24px-full-color/node-type-text.svg";

const icons = {
LayoutSelectTool: { component: LayoutSelectTool, size: 24 },
Expand Down Expand Up @@ -194,6 +195,7 @@ const icons = {
MouseHintMMBDrag: { component: MouseHintMMBDrag, size: 16 },
NodeTypePath: { component: NodeTypePath, size: 24 },
NodeTypeFolder: { component: NodeTypeFolder, size: 24 },
NodeTypeText: { component: NodeTypeText, size: 24 },
};

const components = Object.fromEntries(Object.entries(icons).map(([name, data]) => [name, data.component]));
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const wasm = import("@/../wasm/pkg").then(panicProxy);
window.addEventListener("resize", onWindowResize);
onWindowResize();

document.addEventListener("contextmenu", (e) => e.preventDefault());
// document.addEventListener("contextmenu", (e) => e.preventDefault());
document.addEventListener("fullscreenchange", () => fullscreenModeChanged());

window.addEventListener("keyup", onKeyUp);
Expand Down
23 changes: 22 additions & 1 deletion frontend/src/utilities/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { panicProxy } from "@/utilities/panic-proxy";
const wasm = import("@/../wasm/pkg").then(panicProxy);

let viewportMouseInteractionOngoing = false;
let editingTextField: HTMLTextAreaElement | undefined;

// Keyboard events

Expand Down Expand Up @@ -73,6 +74,13 @@ export async function onMouseMove(e: MouseEvent) {
}

export async function onMouseDown(e: MouseEvent) {
function resize() {
if (editingTextField) {
editingTextField.style.height = "5px";
editingTextField.style.height = `${editingTextField.scrollHeight}px`;
}
}

const target = e.target && (e.target as HTMLElement);
const inCanvas = target && target.closest(".canvas");
const inDialog = target && target.closest(".dialog-modal .floating-menu-content");
Expand All @@ -86,7 +94,20 @@ export async function onMouseDown(e: MouseEvent) {
e.stopPropagation();
}

if (inCanvas) viewportMouseInteractionOngoing = true;
if (inCanvas) {
if (target.nodeName === "TEXTAREA") {
const TextArea = target as HTMLTextAreaElement;
TextArea.addEventListener("input", resize);
editingTextField = TextArea;
} else if (editingTextField) {
if (editingTextField.dataset.path) {
(await wasm).on_input_changed(editingTextField.dataset.path, editingTextField.value, editingTextField.clientWidth, editingTextField.clientHeight);
} else {
console.error("Edited text had not path attribute");
}
editingTextField = undefined;
} else viewportMouseInteractionOngoing = true;
}

if (viewportMouseInteractionOngoing) {
const modifiers = makeModifiersBitfield(e);
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/utilities/response-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ export enum LayerType {
Line = "Line",
PolyLine = "PolyLine",
Ellipse = "Ellipse",
Text = "Text",
}
function newLayerType(input: any): LayerType {
switch (input) {
Expand All @@ -434,6 +435,8 @@ function newLayerType(input: any): LayerType {
return LayerType.PolyLine;
case "Ellipse":
return LayerType.Ellipse;
case "Text":
return LayerType.Text;
default:
throw Error(`Received invalid input as an enum variant for LayerType: ${input}`);
}
Expand Down
7 changes: 7 additions & 0 deletions frontend/wasm/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@ pub fn bounds_of_viewports(bounds_of_viewports: &[f64]) {
dispatch(message);
}

/// When text field is edited
#[wasm_bindgen]
pub fn on_input_changed(path: String, value: String, width: f64, height: f64) {
let message = TextMessage::InputChanged { path, value, size: [width, height] };
dispatch(message);
}

/// Mouse movement within the screenspace bounds of the viewport
#[wasm_bindgen]
pub fn on_mouse_move(x: f64, y: f64, mouse_keys: u8, modifiers: u8) {
Expand Down
1 change: 1 addition & 0 deletions graphene/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ kurbo = { git = "https://github.com/GraphiteEditor/kurbo.git", features = [
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
glam = { version = "0.17", features = ["serde"] }
fonterator = { git = "https://github.com/0HyperCube/fonterator.git" }
Loading