mirror of
https://github.com/penpot/penpot.git
synced 2026-02-12 14:42:56 +00:00
♻️ Refactor minor things
This commit is contained in:
committed by
Elena Torro
parent
a14c36e996
commit
54f63c5dc5
@@ -249,7 +249,7 @@
|
||||
(let [fonts (f/get-content-fonts content)
|
||||
fallback-fonts (fonts-from-text-content content true)
|
||||
all-fonts (concat fonts fallback-fonts)
|
||||
result (f/store-fonts shape-id all-fonts)]
|
||||
result (f/store-fonts all-fonts)]
|
||||
(f/load-fallback-fonts-for-editor! fallback-fonts)
|
||||
(h/call wasm/internal-module "_update_shape_text_layout")
|
||||
result))
|
||||
|
||||
@@ -240,3 +240,12 @@ export const RawGrowType = {
|
||||
"auto-height": 2,
|
||||
};
|
||||
|
||||
export const CursorDirection = {
|
||||
"backward": 0,
|
||||
"forward": 1,
|
||||
"line-before": 2,
|
||||
"line-after": 3,
|
||||
"line-start": 4,
|
||||
"line-end": 5,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
use crate::shapes::{Shape, TextContent, Type, VerticalAlign};
|
||||
use crate::state::{TextCursor, TextEditorState, TextSelection};
|
||||
use crate::state::{TextEditorState, TextSelection};
|
||||
use skia_safe::textlayout::{RectHeightStyle, RectWidthStyle};
|
||||
use skia_safe::{Canvas, Color, Matrix, Paint, Rect};
|
||||
|
||||
const CURSOR_WIDTH: f32 = 1.5;
|
||||
/// FIXME: Use theme color, take into account background color for contrast
|
||||
const SELECTION_COLOR: Color = Color::from_argb(80, 66, 133, 244);
|
||||
const CURSOR_COLOR: Color = Color::BLACK;
|
||||
use skia_safe::{BlendMode, Canvas, Matrix, Paint, Rect};
|
||||
|
||||
pub fn render_overlay(
|
||||
canvas: &Canvas,
|
||||
@@ -26,23 +21,28 @@ pub fn render_overlay(
|
||||
canvas.concat(transform);
|
||||
|
||||
if editor_state.selection.is_selection() {
|
||||
render_selection(canvas, &editor_state.selection, text_content, shape);
|
||||
render_selection(canvas, editor_state, text_content, shape);
|
||||
}
|
||||
|
||||
if editor_state.cursor_visible {
|
||||
render_cursor(canvas, &editor_state.selection.focus, text_content, shape);
|
||||
render_cursor(canvas, editor_state, text_content, shape);
|
||||
}
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
fn render_cursor(canvas: &Canvas, cursor: &TextCursor, text_content: &TextContent, shape: &Shape) {
|
||||
let Some(rect) = calculate_cursor_rect(cursor, text_content, shape) else {
|
||||
fn render_cursor(
|
||||
canvas: &Canvas,
|
||||
editor_state: &TextEditorState,
|
||||
text_content: &TextContent,
|
||||
shape: &Shape,
|
||||
) {
|
||||
let Some(rect) = calculate_cursor_rect(editor_state, text_content, shape) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut paint = Paint::default();
|
||||
paint.set_color(CURSOR_COLOR);
|
||||
paint.set_color(editor_state.theme.cursor_color);
|
||||
paint.set_anti_alias(true);
|
||||
|
||||
canvas.draw_rect(rect, &paint);
|
||||
@@ -50,10 +50,11 @@ fn render_cursor(canvas: &Canvas, cursor: &TextCursor, text_content: &TextConten
|
||||
|
||||
fn render_selection(
|
||||
canvas: &Canvas,
|
||||
selection: &TextSelection,
|
||||
editor_state: &TextEditorState,
|
||||
text_content: &TextContent,
|
||||
shape: &Shape,
|
||||
) {
|
||||
let selection = &editor_state.selection;
|
||||
let rects = calculate_selection_rects(selection, text_content, shape);
|
||||
|
||||
if rects.is_empty() {
|
||||
@@ -61,9 +62,9 @@ fn render_selection(
|
||||
}
|
||||
|
||||
let mut paint = Paint::default();
|
||||
paint.set_color(SELECTION_COLOR);
|
||||
paint.set_blend_mode(BlendMode::Multiply);
|
||||
paint.set_color(editor_state.theme.selection_color);
|
||||
paint.set_anti_alias(true);
|
||||
|
||||
for rect in rects {
|
||||
canvas.draw_rect(rect, &paint);
|
||||
}
|
||||
@@ -82,10 +83,11 @@ fn vertical_align_offset(
|
||||
}
|
||||
|
||||
fn calculate_cursor_rect(
|
||||
cursor: &TextCursor,
|
||||
editor_state: &TextEditorState,
|
||||
text_content: &TextContent,
|
||||
shape: &Shape,
|
||||
) -> Option<Rect> {
|
||||
let cursor = editor_state.selection.focus;
|
||||
let paragraphs = text_content.paragraphs();
|
||||
if cursor.paragraph >= paragraphs.len() {
|
||||
return None;
|
||||
@@ -120,7 +122,7 @@ fn calculate_cursor_rect(
|
||||
} else if char_pos == 0 {
|
||||
let rects = laid_out_para.get_rects_for_range(
|
||||
0..1,
|
||||
RectHeightStyle::Tight,
|
||||
RectHeightStyle::Max,
|
||||
RectWidthStyle::Tight,
|
||||
);
|
||||
if !rects.is_empty() {
|
||||
@@ -131,7 +133,7 @@ fn calculate_cursor_rect(
|
||||
} else if char_pos >= para_char_count {
|
||||
let rects = laid_out_para.get_rects_for_range(
|
||||
para_char_count.saturating_sub(1)..para_char_count,
|
||||
RectHeightStyle::Tight,
|
||||
RectHeightStyle::Max,
|
||||
RectWidthStyle::Tight,
|
||||
);
|
||||
if !rects.is_empty() {
|
||||
@@ -142,7 +144,7 @@ fn calculate_cursor_rect(
|
||||
} else {
|
||||
let rects = laid_out_para.get_rects_for_range(
|
||||
char_pos..char_pos + 1,
|
||||
RectHeightStyle::Tight,
|
||||
RectHeightStyle::Max,
|
||||
RectWidthStyle::Tight,
|
||||
);
|
||||
if !rects.is_empty() {
|
||||
@@ -157,7 +159,7 @@ fn calculate_cursor_rect(
|
||||
return Some(Rect::from_xywh(
|
||||
selrect.x() + cursor_x,
|
||||
selrect.y() + y_offset,
|
||||
CURSOR_WIDTH,
|
||||
editor_state.theme.cursor_width,
|
||||
cursor_height,
|
||||
));
|
||||
}
|
||||
@@ -216,7 +218,7 @@ fn calculate_selection_rects(
|
||||
use skia_safe::textlayout::{RectHeightStyle, RectWidthStyle};
|
||||
let text_boxes = laid_out_para.get_rects_for_range(
|
||||
range_start..range_end,
|
||||
RectHeightStyle::Tight,
|
||||
RectHeightStyle::Max,
|
||||
RectWidthStyle::Tight,
|
||||
);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use crate::shapes::TextPositionWithAffinity;
|
||||
use crate::uuid::Uuid;
|
||||
use skia_safe::Color;
|
||||
|
||||
/// Cursor position within text content.
|
||||
/// Uses character offsets for precise positioning.
|
||||
@@ -105,27 +106,41 @@ pub enum EditorEvent {
|
||||
NeedsLayout = 3,
|
||||
}
|
||||
|
||||
/// FIXME: It should be better to get these constants from the frontend through the API.
|
||||
const SELECTION_COLOR: Color = Color::from_argb(255, 0, 209, 184);
|
||||
const CURSOR_WIDTH: f32 = 1.5;
|
||||
const CURSOR_COLOR: Color = Color::BLACK;
|
||||
const CURSOR_BLINK_INTERVAL_MS: f64 = 530.0;
|
||||
|
||||
pub struct TextEditorTheme {
|
||||
pub selection_color: Color,
|
||||
pub cursor_width: f32,
|
||||
pub cursor_color: Color,
|
||||
}
|
||||
|
||||
pub struct TextEditorState {
|
||||
pub theme: TextEditorTheme,
|
||||
pub selection: TextSelection,
|
||||
pub is_active: bool,
|
||||
pub active_shape_id: Option<Uuid>,
|
||||
pub cursor_visible: bool,
|
||||
pub last_blink_time: f64,
|
||||
pub x_affinity: Option<f32>,
|
||||
pending_events: Vec<EditorEvent>,
|
||||
}
|
||||
|
||||
const CURSOR_BLINK_INTERVAL_MS: f64 = 530.0;
|
||||
|
||||
impl TextEditorState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
theme: TextEditorTheme {
|
||||
selection_color: SELECTION_COLOR,
|
||||
cursor_width: CURSOR_WIDTH,
|
||||
cursor_color: CURSOR_COLOR,
|
||||
},
|
||||
selection: TextSelection::new(),
|
||||
is_active: false,
|
||||
active_shape_id: None,
|
||||
cursor_visible: true,
|
||||
last_blink_time: 0.0,
|
||||
x_affinity: None,
|
||||
pending_events: Vec::new(),
|
||||
}
|
||||
}
|
||||
@@ -136,7 +151,6 @@ impl TextEditorState {
|
||||
self.cursor_visible = true;
|
||||
self.last_blink_time = 0.0;
|
||||
self.selection = TextSelection::new();
|
||||
self.x_affinity = None;
|
||||
self.pending_events.clear();
|
||||
}
|
||||
|
||||
@@ -144,7 +158,6 @@ impl TextEditorState {
|
||||
self.is_active = false;
|
||||
self.active_shape_id = None;
|
||||
self.cursor_visible = false;
|
||||
self.x_affinity = None;
|
||||
self.pending_events.clear();
|
||||
}
|
||||
|
||||
@@ -152,7 +165,6 @@ impl TextEditorState {
|
||||
let cursor = TextCursor::new(position.paragraph as usize, position.offset as usize);
|
||||
self.selection.set_caret(cursor);
|
||||
self.reset_blink();
|
||||
self.clear_x_affinity();
|
||||
self.push_event(EditorEvent::SelectionChanged);
|
||||
}
|
||||
|
||||
@@ -186,10 +198,6 @@ impl TextEditorState {
|
||||
self.last_blink_time = 0.0;
|
||||
}
|
||||
|
||||
pub fn clear_x_affinity(&mut self) {
|
||||
self.x_affinity = None;
|
||||
}
|
||||
|
||||
pub fn push_event(&mut self, event: EditorEvent) {
|
||||
if self.pending_events.last() != Some(&event) {
|
||||
self.pending_events.push(event);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use macros::ToJs;
|
||||
|
||||
use crate::math::{Matrix, Point, Rect};
|
||||
use crate::mem;
|
||||
use crate::shapes::{Paragraph, Shape, TextContent, Type, VerticalAlign};
|
||||
@@ -6,6 +8,18 @@ use crate::utils::uuid_from_u32_quartet;
|
||||
use crate::utils::uuid_to_u32_quartet;
|
||||
use crate::{with_state, with_state_mut, STATE};
|
||||
|
||||
#[derive(PartialEq, ToJs)]
|
||||
#[repr(u8)]
|
||||
#[allow(dead_code)]
|
||||
pub enum CursorDirection {
|
||||
Backward = 0,
|
||||
Forward = 1,
|
||||
LineBefore = 2,
|
||||
LineAfter = 3,
|
||||
LineStart = 4,
|
||||
LineEnd = 5,
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// STATE MANAGEMENT
|
||||
// ============================================================================
|
||||
@@ -462,7 +476,7 @@ pub extern "C" fn text_editor_insert_paragraph() {
|
||||
// ============================================================================
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_move_cursor(direction: u8, extend_selection: bool) {
|
||||
pub extern "C" fn text_editor_move_cursor(direction: CursorDirection, extend_selection: bool) {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.is_active {
|
||||
return;
|
||||
@@ -488,13 +502,16 @@ pub extern "C" fn text_editor_move_cursor(direction: u8, extend_selection: bool)
|
||||
let current = state.text_editor_state.selection.focus;
|
||||
|
||||
let new_cursor = match direction {
|
||||
0 => move_cursor_left(¤t, paragraphs),
|
||||
1 => move_cursor_right(¤t, paragraphs),
|
||||
2 => move_cursor_up(¤t, paragraphs, text_content, shape),
|
||||
3 => move_cursor_down(¤t, paragraphs, text_content, shape),
|
||||
4 => move_cursor_line_start(¤t, paragraphs),
|
||||
5 => move_cursor_line_end(¤t, paragraphs),
|
||||
_ => current,
|
||||
CursorDirection::Backward => move_cursor_backward(¤t, paragraphs),
|
||||
CursorDirection::Forward => move_cursor_forward(¤t, paragraphs),
|
||||
CursorDirection::LineBefore => {
|
||||
move_cursor_up(¤t, paragraphs, text_content, shape)
|
||||
}
|
||||
CursorDirection::LineAfter => {
|
||||
move_cursor_down(¤t, paragraphs, text_content, shape)
|
||||
}
|
||||
CursorDirection::LineStart => move_cursor_line_start(¤t, paragraphs),
|
||||
CursorDirection::LineEnd => move_cursor_line_end(¤t, paragraphs),
|
||||
};
|
||||
|
||||
if extend_selection {
|
||||
@@ -504,11 +521,6 @@ pub extern "C" fn text_editor_move_cursor(direction: u8, extend_selection: bool)
|
||||
}
|
||||
|
||||
state.text_editor_state.reset_blink();
|
||||
|
||||
if direction == 0 || direction == 1 || direction == 4 || direction == 5 {
|
||||
state.text_editor_state.clear_x_affinity();
|
||||
}
|
||||
|
||||
state
|
||||
.text_editor_state
|
||||
.push_event(crate::state::EditorEvent::SelectionChanged);
|
||||
@@ -944,7 +956,7 @@ fn clamp_cursor(cursor: TextCursor, paragraphs: &[Paragraph]) -> TextCursor {
|
||||
}
|
||||
|
||||
/// Move cursor left by one character.
|
||||
fn move_cursor_left(cursor: &TextCursor, paragraphs: &[Paragraph]) -> TextCursor {
|
||||
fn move_cursor_backward(cursor: &TextCursor, paragraphs: &[Paragraph]) -> TextCursor {
|
||||
if cursor.char_offset > 0 {
|
||||
TextCursor::new(cursor.paragraph, cursor.char_offset - 1)
|
||||
} else if cursor.paragraph > 0 {
|
||||
@@ -957,7 +969,7 @@ fn move_cursor_left(cursor: &TextCursor, paragraphs: &[Paragraph]) -> TextCursor
|
||||
}
|
||||
|
||||
/// Move cursor right by one character.
|
||||
fn move_cursor_right(cursor: &TextCursor, paragraphs: &[Paragraph]) -> TextCursor {
|
||||
fn move_cursor_forward(cursor: &TextCursor, paragraphs: &[Paragraph]) -> TextCursor {
|
||||
let para = ¶graphs[cursor.paragraph];
|
||||
let char_count = paragraph_char_count(para);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user