mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 05:50:35 +00:00
🐛 Sync Design tab styles on text-editor click caret (#12205)
Click caret placement only moved the selection and never refreshed current styles, so the Design tab kept showing the previous fill and typography. Place the caret through place_caret_from_position so StylesChanged is emitted like keyboard navigation. Closes #12195
This commit is contained in:
parent
2ffdd16038
commit
78d26243c7
@ -566,6 +566,16 @@ impl TextEditorState {
|
||||
self.push_event(TextEditorEvent::SelectionChanged);
|
||||
}
|
||||
|
||||
/// Places the caret and refreshes `current_styles` (emits `StylesChanged`).
|
||||
pub fn place_caret_from_position(
|
||||
&mut self,
|
||||
text_content: &TextContent,
|
||||
position: &TextPositionWithAffinity,
|
||||
) {
|
||||
self.set_caret_from_position(position);
|
||||
self.update_styles(text_content);
|
||||
}
|
||||
|
||||
pub fn extend_selection_from_position(&mut self, position: &TextPositionWithAffinity) {
|
||||
self.selection.extend_to(*position);
|
||||
self.reset_blink();
|
||||
@ -940,3 +950,125 @@ impl Default for TextEditorState {
|
||||
fn is_word_char(c: char) -> bool {
|
||||
c.is_alphanumeric() || c == '_'
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::math::Rect;
|
||||
use crate::shapes::{
|
||||
Fill, FontFamily, FontStyle, GrowType, Paragraph, SolidColor, TextAlign, TextSpan,
|
||||
};
|
||||
use skia_safe::Color;
|
||||
|
||||
fn span(text: &str, font_size: f32, fill: Fill) -> TextSpan {
|
||||
TextSpan::new(
|
||||
text.to_string(),
|
||||
FontFamily::new(Uuid::nil(), 400, FontStyle::Normal),
|
||||
font_size,
|
||||
1.2,
|
||||
0.0,
|
||||
None,
|
||||
None,
|
||||
TextDirection::LTR,
|
||||
400,
|
||||
Uuid::nil(),
|
||||
vec![fill],
|
||||
)
|
||||
}
|
||||
|
||||
fn hello_world_content() -> TextContent {
|
||||
let black = Fill::Solid(SolidColor(Color::BLACK));
|
||||
let red = Fill::Solid(SolidColor(Color::RED));
|
||||
let mut content =
|
||||
TextContent::new(Rect::from_xywh(0.0, 0.0, 200.0, 100.0), GrowType::Fixed);
|
||||
content.add_paragraph(Paragraph::new(
|
||||
TextAlign::Left,
|
||||
TextDirection::LTR,
|
||||
None,
|
||||
None,
|
||||
1.2,
|
||||
0.0,
|
||||
vec![span("Hello ", 24.0, black), span("world", 40.0, red)],
|
||||
));
|
||||
content
|
||||
}
|
||||
|
||||
fn drain_events(editor: &mut TextEditorState) -> Vec<TextEditorEvent> {
|
||||
let mut events = Vec::new();
|
||||
loop {
|
||||
let ev = editor.poll_event();
|
||||
if ev == TextEditorEvent::None {
|
||||
break;
|
||||
}
|
||||
events.push(ev);
|
||||
}
|
||||
events
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_caret_from_position_alone_does_not_emit_styles_changed() {
|
||||
let content = hello_world_content();
|
||||
let mut editor = TextEditorState::new();
|
||||
editor.place_caret_from_position(
|
||||
&content,
|
||||
&TextPositionWithAffinity::new_downstream_affinity(0, 1),
|
||||
);
|
||||
drain_events(&mut editor);
|
||||
|
||||
editor.set_caret_from_position(&TextPositionWithAffinity::new_downstream_affinity(0, 8));
|
||||
let events = drain_events(&mut editor);
|
||||
|
||||
assert!(!events.contains(&TextEditorEvent::StylesChanged));
|
||||
assert_eq!(editor.current_styles.font_size.value(), &Some(24.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn place_caret_from_position_syncs_styles_and_emits_styles_changed() {
|
||||
let content = hello_world_content();
|
||||
let mut editor = TextEditorState::new();
|
||||
editor.place_caret_from_position(
|
||||
&content,
|
||||
&TextPositionWithAffinity::new_downstream_affinity(0, 1),
|
||||
);
|
||||
drain_events(&mut editor);
|
||||
assert_eq!(editor.current_styles.font_size.value(), &Some(24.0));
|
||||
|
||||
// Offset 8 is inside the second span ("world").
|
||||
editor.place_caret_from_position(
|
||||
&content,
|
||||
&TextPositionWithAffinity::new_downstream_affinity(0, 8),
|
||||
);
|
||||
let events = drain_events(&mut editor);
|
||||
|
||||
assert!(events.contains(&TextEditorEvent::StylesChanged));
|
||||
assert_eq!(editor.current_styles.font_size.value(), &Some(40.0));
|
||||
assert_eq!(
|
||||
editor.current_styles.fills,
|
||||
vec![Fill::Solid(SolidColor(Color::RED))]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn place_caret_from_position_clears_mixed_selection_styles() {
|
||||
let content = hello_world_content();
|
||||
let mut editor = TextEditorState::new();
|
||||
editor.select_all(&content);
|
||||
assert!(editor.current_styles.font_size.is_multiple());
|
||||
assert!(editor.current_styles.fills_are_multiple);
|
||||
drain_events(&mut editor);
|
||||
|
||||
editor.place_caret_from_position(
|
||||
&content,
|
||||
&TextPositionWithAffinity::new_downstream_affinity(0, 8),
|
||||
);
|
||||
let events = drain_events(&mut editor);
|
||||
|
||||
assert!(events.contains(&TextEditorEvent::StylesChanged));
|
||||
assert_eq!(editor.current_styles.font_size.value(), &Some(40.0));
|
||||
assert!(!editor.current_styles.fills_are_multiple);
|
||||
assert_eq!(
|
||||
editor.current_styles.fills,
|
||||
vec![Fill::Solid(SolidColor(Color::RED))]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,8 +208,7 @@ pub extern "C" fn text_editor_pointer_down(x: f32, y: f32) {
|
||||
let point = Point::new(x, y);
|
||||
get_text_editor_state().start_pointer_selection();
|
||||
if let Some(position) = text_content.get_caret_position_from_shape_coords(&point) {
|
||||
get_text_editor_state().set_caret_from_position(&position);
|
||||
get_text_editor_state().update_styles(text_content);
|
||||
get_text_editor_state().place_caret_from_position(text_content, &position);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -332,7 +331,7 @@ pub extern "C" fn text_editor_set_cursor_from_offset(x: f32, y: f32) {
|
||||
};
|
||||
|
||||
if let Some(position) = text_content.get_caret_position_from_shape_coords(&point) {
|
||||
get_text_editor_state().set_caret_from_position(&position);
|
||||
get_text_editor_state().place_caret_from_position(text_content, &position);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -359,7 +358,7 @@ pub extern "C" fn text_editor_set_cursor_from_point(x: f32, y: f32) {
|
||||
if let Some(position) =
|
||||
text_content.get_caret_position_from_screen_coords(&point, &view_matrix, &shape_matrix)
|
||||
{
|
||||
get_text_editor_state().set_caret_from_position(&position);
|
||||
get_text_editor_state().place_caret_from_position(text_content, &position);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user