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

Implement IME primitives for PlainEditor. #136

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
21 changes: 19 additions & 2 deletions examples/vello_editor/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ use vello::util::{RenderContext, RenderSurface};
use vello::wgpu;
use vello::{AaConfig, Renderer, RendererOptions, Scene};
use winit::application::ApplicationHandler;
use winit::dpi::LogicalSize;
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::event::*;
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::window::Window;

// #[path = "text2.rs"]
mod text;
use parley::{GenericFamily, PlainEditorOp, StyleProperty};
use parley::{GenericFamily, PlainEditorOp, Rect, StyleProperty};

// Simple struct to hold the state of the renderer
pub struct ActiveRenderState<'s> {
Expand Down Expand Up @@ -85,6 +85,8 @@ impl ApplicationHandler for SimpleVelloApp<'_> {
self.renderers[surface.dev_id]
.get_or_insert_with(|| create_vello_renderer(&self.context, &surface));

window.set_ime_allowed(true);

// Save the Window and Surface to a state variable
self.state = RenderState::Active(ActiveRenderState { window, surface });

Expand Down Expand Up @@ -116,6 +118,21 @@ impl ApplicationHandler for SimpleVelloApp<'_> {

self.editor.handle_event(event.clone());
if self.last_drawn_generation != self.editor.generation() {
if let Some(Rect { x0, y0, x1, y1 }) = self.editor.preedit_area() {
// `Window::set_ime_cursor_area` is broken on X11 as of winit 0.30.5
// see <https://github.com/rust-windowing/winit/issues/3965>.
//
// render_state.window.set_ime_cursor_area(
// PhysicalPosition::new(x0, y0),
// PhysicalSize::new(x1 - x0, y1 - y0),
// );

// Not providing a size, and putting the position at the bottom left corner
// instead of the top left corner (as docs suggest) works around this for now.
render_state
.window
.set_ime_cursor_area(PhysicalPosition::new(x0, y1), PhysicalSize::new(0, 0));
}
render_state.window.request_redraw();
}
// render_state
Expand Down
39 changes: 36 additions & 3 deletions examples/vello_editor/src/text.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// Copyright 2024 the Parley Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT

use parley::layout::PositionedLayoutItem;
use peniko::{kurbo::Affine, Color, Fill};
use std::time::Instant;
use vello::Scene;
use winit::{
event::{Modifiers, Touch, WindowEvent},
event::{Ime, Modifiers, Touch, WindowEvent},
keyboard::{Key, NamedKey},
};

Expand All @@ -16,7 +15,7 @@ use alloc::{sync::Arc, vec};
use core::{default::Default, iter::IntoIterator};

pub use parley::layout::editor::Generation;
use parley::{FontContext, LayoutContext, PlainEditor, PlainEditorOp};
use parley::{FontContext, LayoutContext, PlainEditor, PlainEditorOp, PositionedLayoutItem, Rect};

pub const INSET: f32 = 32.0;

Expand All @@ -42,6 +41,17 @@ impl Editor {
self.editor.text()
}

pub fn preedit_area(&self) -> Option<Rect> {
self.editor.preedit_area().map(|r| {
Rect::new(
r.x0 + INSET as f64,
r.y0 + INSET as f64,
r.x1 + INSET as f64,
r.y1 + INSET as f64,
)
})
}

pub fn handle_event(&mut self, event: WindowEvent) {
match event {
WindowEvent::Resized(size) => {
Expand All @@ -56,6 +66,26 @@ impl Editor {
WindowEvent::ModifiersChanged(modifiers) => {
self.modifiers = Some(modifiers);
}
WindowEvent::Ime(ime) => {
self.editor.transact(
&mut self.font_cx,
&mut self.layout_cx,
match ime {
Ime::Commit(text) => {
Some(PlainEditorOp::InsertOrReplaceSelection(text.into()))
}
Ime::Disabled => Some(PlainEditorOp::ClearCompose),
Ime::Preedit(text, _) if text.is_empty() => {
Some(PlainEditorOp::ClearCompose)
}
Ime::Preedit(text, cursor) => Some(PlainEditorOp::SetCompose {
text: text.into(),
cursor,
}),
_ => None,
},
);
}
WindowEvent::KeyboardInput { event, .. } => {
if !event.state.is_pressed() {
return;
Expand Down Expand Up @@ -305,6 +335,9 @@ impl Editor {
if let Some(cursor) = self.editor.selection_weak_geometry(1.5) {
scene.fill(Fill::NonZero, transform, Color::LIGHT_GRAY, None, &cursor);
};
for rect in self.editor.preedit_underline_geometry(1.5).iter() {
scene.fill(Fill::NonZero, transform, Color::WHITE, None, &rect);
}
for line in self.editor.lines() {
for item in line.items() {
let PositionedLayoutItem::GlyphRun(glyph_run) = item else {
Expand Down
28 changes: 18 additions & 10 deletions parley/src/layout/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,10 @@ impl Selection {
/// Returns the index where text should be inserted based on this
/// selection.
pub fn insertion_index(&self) -> usize {
self.focus.text_start as usize
(match self.focus.affinity {
Affinity::Upstream => self.focus.text_end,
Affinity::Downstream => self.focus.text_start,
}) as usize
}

/// Returns a new collapsed selection at the position of the current
Expand Down Expand Up @@ -531,7 +534,7 @@ impl Selection {
/// visual order.
///
/// If `extend` is `true` then the current anchor will be retained,
/// otherwise the new selection will be collapsed.
/// otherwise the new selection will be collapsed.
#[must_use]
pub fn previous_visual<B: Brush>(
&self,
Expand All @@ -552,7 +555,7 @@ impl Selection {
/// Returns a new selection with the focus moved to the next word.
///
/// If `extend` is `true` then the current anchor will be retained,
/// otherwise the new selection will be collapsed.
/// otherwise the new selection will be collapsed.
#[must_use]
pub fn next_word<B: Brush>(&self, layout: &Layout<B>, extend: bool) -> Self {
self.maybe_extend(self.focus.next_word(layout), extend)
Expand All @@ -561,13 +564,18 @@ impl Selection {
/// Returns a new selection with the focus moved to the previous word.
///
/// If `extend` is `true` then the current anchor will be retained,
/// otherwise the new selection will be collapsed.
/// otherwise the new selection will be collapsed.
#[must_use]
pub fn previous_word<B: Brush>(&self, layout: &Layout<B>, extend: bool) -> Self {
self.maybe_extend(self.focus.previous_word(layout), extend)
}

fn maybe_extend(&self, focus: Cursor, extend: bool) -> Self {
/// Returns a new selection with the focus moved to another cursor.
///
/// If `extend` is `true` then the current anchor will be retained,
/// otherwise the new selection will be collapsed.
#[must_use]
pub fn maybe_extend(&self, focus: Cursor, extend: bool) -> Self {
if extend {
Self {
anchor: self.anchor,
Expand All @@ -583,7 +591,7 @@ impl Selection {
/// current line.
///
/// If `extend` is `true` then the current anchor will be retained,
/// otherwise the new selection will be collapsed.
/// otherwise the new selection will be collapsed.
#[must_use]
pub fn line_start<B: Brush>(&self, layout: &Layout<B>, extend: bool) -> Self {
if let Some(line) = self.focus.path.line(layout) {
Expand All @@ -600,7 +608,7 @@ impl Selection {
/// current line.
///
/// If `extend` is `true` then the current anchor will be retained,
/// otherwise the new selection will be collapsed.
/// otherwise the new selection will be collapsed.
#[must_use]
pub fn line_end<B: Brush>(&self, layout: &Layout<B>, extend: bool) -> Self {
if let Some(line) = self.focus.path.line(layout) {
Expand All @@ -621,7 +629,7 @@ impl Selection {
/// current horizontal position will be maintained.
///
/// If `extend` is `true` then the current anchor will be retained,
/// otherwise the new selection will be collapsed.
/// otherwise the new selection will be collapsed.
#[must_use]
pub fn next_line<B: Brush>(&self, layout: &Layout<B>, extend: bool) -> Self {
self.move_lines(layout, 1, extend)
Expand All @@ -631,7 +639,7 @@ impl Selection {
/// current horizontal position will be maintained.
///
/// If `extend` is `true` then the current anchor will be retained,
/// otherwise the new selection will be collapsed.
/// otherwise the new selection will be collapsed.
#[must_use]
pub fn previous_line<B: Brush>(&self, layout: &Layout<B>, extend: bool) -> Self {
self.move_lines(layout, -1, extend)
Expand All @@ -645,7 +653,7 @@ impl Selection {
/// toward next lines.
///
/// If `extend` is `true` then the current anchor will be retained,
/// otherwise the new selection will be collapsed.
/// otherwise the new selection will be collapsed.
#[must_use]
pub fn move_lines<B: Brush>(&self, layout: &Layout<B>, delta: isize, extend: bool) -> Self {
if delta == 0 {
Expand Down
Loading
Loading