Skip to content

Commit

Permalink
feat: spellcheck correction picker
Browse files Browse the repository at this point in the history
  • Loading branch information
Kneemund committed Nov 27, 2024
1 parent 96dce63 commit 4b1ef0c
Show file tree
Hide file tree
Showing 11 changed files with 364 additions and 15 deletions.
35 changes: 35 additions & 0 deletions crates/rnote-engine/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,41 @@ impl Engine {
widget_flags
}

pub fn get_spellcheck_corrections(&self) -> Option<Vec<String>> {
if let Pen::Typewriter(typewriter) = self.penholder.current_pen_ref() {
return typewriter.get_spellcheck_correction_in_modifying_stroke(&mut EngineView {
tasks_tx: self.tasks_tx.clone(),
pens_config: &self.pens_config,
document: &self.document,
store: &self.store,
camera: &self.camera,
audioplayer: &self.audioplayer,
spellcheck: &self.spellcheck,
});
}

None
}

pub fn apply_spellcheck_correction(&mut self, correction: &str) -> WidgetFlags {
if let Pen::Typewriter(typewriter) = self.penholder.current_pen_mut() {
return typewriter.apply_spellcheck_correction_in_modifying_stroke(
correction,
&mut EngineViewMut {
tasks_tx: self.tasks_tx.clone(),
pens_config: &mut self.pens_config,
document: &mut self.document,
store: &mut self.store,
camera: &mut self.camera,
audioplayer: &mut self.audioplayer,
spellcheck: &mut self.spellcheck,
},
);
}

WidgetFlags::default()
}

pub fn optimize_epd(&self) -> bool {
self.optimize_epd
}
Expand Down
2 changes: 1 addition & 1 deletion crates/rnote-engine/src/pens/penholder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ impl PenHolder {
self.progress
}

pub fn current_pen_ref(&mut self) -> &Pen {
pub fn current_pen_ref(&self) -> &Pen {
&self.current_pen
}

Expand Down
53 changes: 53 additions & 0 deletions crates/rnote-engine/src/pens/typewriter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,59 @@ impl Typewriter {
}
}

pub(crate) fn get_spellcheck_correction_in_modifying_stroke(
&self,
engine_view: &EngineView,
) -> Option<Vec<String>> {
if let TypewriterState::Modifying {
stroke_key, cursor, ..
} = &self.state
{
if let Some(Stroke::TextStroke(textstroke)) =
engine_view.store.get_stroke_ref(*stroke_key)
{
return textstroke.get_spellcheck_corrections_at_index(
engine_view.spellcheck,
cursor.cur_cursor(),
);
}
}

None
}

pub(crate) fn apply_spellcheck_correction_in_modifying_stroke(
&mut self,
correction: &str,
engine_view: &mut EngineViewMut,
) -> WidgetFlags {
let mut widget_flags = WidgetFlags::default();

if let TypewriterState::Modifying {
stroke_key, cursor, ..
} = &mut self.state
{
if let Some(Stroke::TextStroke(textstroke)) =
engine_view.store.get_stroke_mut(*stroke_key)
{
textstroke.apply_spellcheck_correction_at_cursor(cursor, correction);

engine_view.store.update_geometry_for_stroke(*stroke_key);
engine_view.store.regenerate_rendering_for_stroke(
*stroke_key,
engine_view.camera.viewport(),
engine_view.camera.image_scale(),
);

widget_flags |= engine_view.store.record(Instant::now());
widget_flags.redraw = true;
widget_flags.store_modified = true;
}
}

widget_flags
}

pub(crate) fn toggle_text_attribute_current_selection(
&mut self,
text_attribute: TextAttribute,
Expand Down
54 changes: 52 additions & 2 deletions crates/rnote-engine/src/strokes/textstroke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,8 @@ impl TextStyle {
camera: &Camera,
) {
const ERROR_COLOR: piet::Color = color::GNOME_REDS[2];
const STYLE: piet::StrokeStyle = piet::StrokeStyle::new().line_cap(piet::LineCap::Round);

let scale = 1.0 / camera.total_zoom();

if let Ok(selection_rects) =
Expand All @@ -433,7 +435,7 @@ impl TextStyle {
);

let path = create_wavy_line(origin, width, scale);
cx.stroke(path, &ERROR_COLOR, 1.5 * scale);
cx.stroke_styled(path, &ERROR_COLOR, 1.5 * scale, &STYLE);
}
}
}
Expand Down Expand Up @@ -685,6 +687,52 @@ impl TextStroke {
}
}

pub fn get_spellcheck_corrections_at_index(
&self,
spellcheck: &Spellcheck,
index: usize,
) -> Option<Vec<String>> {
let Some(dict) = &spellcheck.dict else {
return None;
};

let start_index = self.get_prev_word_start_index(index);

if let Some(length) = self.spellcheck_result.errors.get(&start_index) {
let word = self.get_text_slice_for_range(start_index..start_index + length);
return Some(dict.suggest(word));
}

None
}

pub fn apply_spellcheck_correction_at_cursor(
&mut self,
cursor: &mut GraphemeCursor,
correction: &str,
) {
let cur_pos = cursor.cur_cursor();
let start_index = self.get_prev_word_start_index(cur_pos);

if let Some(length) = self.spellcheck_result.errors.get(&start_index) {
let old_length = *length;
let new_length = correction.len();

self.text
.replace_range(start_index..start_index + old_length, correction);

self.spellcheck_result.errors.remove(&start_index);

// translate the text attributes
self.translate_attrs_after_cursor(
start_index + old_length,
(new_length as i32) - (old_length as i32),
);

*cursor = GraphemeCursor::new(start_index + new_length, self.text.len(), true);
}
}

pub fn check_spelling_range(
&mut self,
start_index: usize,
Expand Down Expand Up @@ -870,7 +918,9 @@ impl TextStroke {
};

for (word_start, word_length) in translated_words {
let new_word_start = word_start.saturating_add_signed(offset as isize);
let Some(new_word_start) = word_start.checked_add_signed(offset as isize) else {
continue;
};

if new_word_start >= from_pos {
self.spellcheck_result
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions crates/rnote-ui/data/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ rnote_ui_gresources_icons_files = files(
'icons/scalable/actions/text-indent-less-symbolic.svg',
'icons/scalable/actions/text-indent-more-symbolic.svg',
'icons/scalable/actions/text-italic-symbolic.svg',
'icons/scalable/actions/text-squiggly-symbolic.svg',
'icons/scalable/actions/text-strikethrough-symbolic.svg',
'icons/scalable/actions/text-underline-symbolic.svg',
'icons/scalable/actions/touch-two-finger-long-press-symbolic.svg',
Expand Down
1 change: 1 addition & 0 deletions crates/rnote-ui/data/resources.gresource.xml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
<file compressed="true">icons/scalable/actions/text-indent-less-symbolic.svg</file>
<file compressed="true">icons/scalable/actions/text-indent-more-symbolic.svg</file>
<file compressed="true">icons/scalable/actions/text-italic-symbolic.svg</file>
<file compressed="true">icons/scalable/actions/text-squiggly-symbolic.svg</file>
<file compressed="true">icons/scalable/actions/text-strikethrough-symbolic.svg</file>
<file compressed="true">icons/scalable/actions/text-underline-symbolic.svg</file>
<file compressed="true">icons/scalable/actions/touch-two-finger-long-press-symbolic.svg</file>
Expand Down
93 changes: 93 additions & 0 deletions crates/rnote-ui/data/ui/penssidebar/typewriterpage.ui
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,99 @@
<object class="GtkEmojiChooser" id="emojichooser">
<property name="position">right</property>
</object>
<child>
<object class="GtkMenuButton" id="spellcheck_corrections_menubutton">
<property name="direction">left</property>
<property name="tooltip_text" translatable="yes">Spellcheck Corrections</property>
<property name="icon-name">text-squiggly-symbolic</property>
<style>
<class name="flat" />
<class name="sidebar_action_button" />
</style>
</object>
</child>
<object class="GtkPopover" id="spellcheck_corrections">
<child>
<object class="GtkListView" id="spellcheck_corrections_listview">
<property name="single-click-activate">true</property>
<style>
<class name="navigation-sidebar" />
</style>
</object>
</child>
</object>
<object class="GtkPopover" id="spellcheck_corrections_unavailable">
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<property name="margin-start">18</property>
<property name="margin-end">18</property>
<property name="margin-top">18</property>
<property name="margin-bottom">18</property>
<child>
<object class="GtkImage">
<property name="icon-name">text-squiggly-symbolic</property>
<property name="pixel-size">64</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="justify">center</property>
<property name="label" translatable="yes">No Corrections
Available</property>
<style>
<class name="title-4"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="justify">center</property>
<property name="label" translatable="yes">Move cursor over underlined
words to get suggestions.</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
</object>
</child>
</object>
<object class="GtkPopover" id="spellcheck_corrections_empty">
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<property name="margin-start">18</property>
<property name="margin-end">18</property>
<property name="margin-top">18</property>
<property name="margin-bottom">18</property>
<child>
<object class="GtkImage">
<property name="icon-name">text-squiggly-symbolic</property>
<property name="pixel-size">64</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="justify">center</property>
<property name="label" translatable="yes">No Corrections
Found</property>
<style>
<class name="title-4"/>
</style>
</object>
</child>
</object>
</child>
</object>
<child>
<object class="GtkSeparator">
<property name="orientation">vertical</property>
Expand Down
10 changes: 10 additions & 0 deletions crates/rnote-ui/src/appwindow/imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,11 @@ impl RnAppWindow {
.typewriter_page()
.emojichooser_menubutton()
.set_direction(ArrowType::Right);
obj.overlays()
.penssidebar()
.typewriter_page()
.spellcheck_corrections_menubutton()
.set_direction(ArrowType::Right);
obj.overlays()
.penssidebar()
.eraser_page()
Expand Down Expand Up @@ -762,6 +767,11 @@ impl RnAppWindow {
.typewriter_page()
.emojichooser_menubutton()
.set_direction(ArrowType::Left);
obj.overlays()
.penssidebar()
.typewriter_page()
.spellcheck_corrections_menubutton()
.set_direction(ArrowType::Left);
obj.overlays()
.penssidebar()
.eraser_page()
Expand Down
Loading

0 comments on commit 4b1ef0c

Please sign in to comment.