Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

feat: add IME preedit support #2198

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
Loading
from
Open
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
58 changes: 56 additions & 2 deletions 58 src/renderer/cursor_renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
editor::{Cursor, CursorShape},
profiling::{tracy_plot, tracy_zone},
renderer::animation_utils::*,
renderer::{GridRenderer, RenderedWindow},
renderer::{GridRenderer, Preedit, RenderedWindow},
settings::{ParseFromValue, SETTINGS},
window::{ShouldRender, UserEvent},
};
Expand Down Expand Up @@ -284,7 +284,13 @@ impl CursorRenderer {
self.blink_status.update_status(&self.cursor)
}

pub fn draw(&mut self, grid_renderer: &mut GridRenderer, canvas: &Canvas) {
pub fn draw(
&mut self,
grid_renderer: &mut GridRenderer,
canvas: &Canvas,
preedit: Option<&Preedit>,
w: f32,
) {
tracy_zone!("cursor_draw");
let render = self.blink_status.should_render();
let settings = SETTINGS.get::<CursorSettings>();
Expand Down Expand Up @@ -341,6 +347,54 @@ impl CursorRenderer {

canvas.restore();

// draw ime if possible under current cursor
if let Some(preedit) = preedit {
let factor = grid_renderer.font_dimensions.width as f32;
let y_adjustment = y_adjustment as f32 + factor / 5.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this factor of 5.0 do?


let off = if let Some(off) = preedit.cursor_offset() {
off as f32 * factor
} else {
0f32
};

let mut start = self.destination.x;
let mut end = self.destination.x + off;
if end + factor > w {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ideally we should use the neovim grid window width here, not the global window width, otherwise it will render outside floating window and splits for example.

This als means that the code better belongs in rendered_window, where the width and tranaparency needed is available.

start = start - (end + factor - w);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This clippy warning should be fixed

end = w - factor;
}

use skia_safe::Rect;
let r = Rect::new(
start,
self.destination.y,
end,
self.destination.y + y_adjustment + factor / 4.0,
);

paint.set_color(background_color);
canvas.draw_rect(r, &paint);

let blobs =
&grid_renderer
.shaper
.shape_cached(preedit.preedit_text().clone(), bold, italic);
paint.set_color(foreground_color);
for blob in blobs.iter() {
canvas.draw_text_blob(blob, (start, self.destination.y + y_adjustment), &paint);
}

paint.set_color(skia_safe::colors::GREY.to_color());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use the correct highlight group color, otherwise it might be invisible. I think this is the selection right?

Also, shouldn’t it be drawn before the text? So that the text remain invisible, with the text color also set apporopriately through the highlight groups.

I also think that the floating window transparency need to be taken into account.

let r = Rect::new(
start,
self.destination.y + y_adjustment,
end,
self.destination.y + y_adjustment + factor / 4.0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this factor 4.0 do?

);
canvas.draw_rect(r, &paint);
}

if let Some(vfx) = self.cursor_vfx.as_ref() {
vfx.render(&settings, canvas, grid_renderer, &self.cursor);
}
Expand Down
82 changes: 82 additions & 0 deletions 82 src/renderer/ime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use winit::dpi::PhysicalPosition;
#[derive(Debug, Default)]
pub struct Ime {
/// Whether the IME is enabled.
enabled: bool,

/// Current IME preedit.
preedit: Option<Preedit>,

/// IME position
position: PhysicalPosition<i32>,
}

impl Ime {
pub fn new() -> Self {
Default::default()
}

#[inline]
pub fn set_enabled(&mut self, is_enabled: bool) {
if is_enabled {
self.enabled = is_enabled
} else {
// clear all and create new
*self = Default::default();
}
}

#[inline]
pub fn is_enabled(&self) -> bool {
self.enabled
}

#[inline]
pub fn set_preedit(&mut self, preedit: Option<Preedit>) {
self.preedit = preedit;
}

#[inline]
pub fn preedit(&self) -> Option<&Preedit> {
self.preedit.as_ref()
}

#[inline]
pub fn set_position(&mut self, p: PhysicalPosition<i32>) {
self.position = p;
}

#[inline]
pub fn position(&self) -> PhysicalPosition<i32> {
return self.position;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This clippy warning should be fixed

}
}

#[derive(Debug, Default, PartialEq, Eq)]
pub struct Preedit {
/// The preedit text.
text: String,

/// Byte offset from cusor start
/// `None` means that the cursor is invisible.
cursor_offset: Option<usize>,
}

impl Preedit {
pub fn new(text: String, cursor_offset: Option<usize>) -> Self {
Self {
text,
cursor_offset,
}
}
pub fn preedit_text(&self) -> &String {
&self.text
}
pub fn cursor_offset(&self) -> Option<usize> {
if let Some(offset) = self.cursor_offset {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This clippy warning should be fixed

Some(offset)
} else {
None
}
}
}
6 changes: 4 additions & 2 deletions 6 src/renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod animation_utils;
pub mod cursor_renderer;
pub mod fonts;
pub mod grid_renderer;
pub mod ime;
mod opengl;
pub mod profiler;
mod rendered_window;
Expand Down Expand Up @@ -30,6 +31,7 @@ use crate::{
use cursor_renderer::CursorRenderer;
pub use fonts::caching_shaper::CachingShaper;
pub use grid_renderer::GridRenderer;
pub use ime::{Ime, Preedit};
pub use rendered_window::{LineFragment, RenderedWindow, WindowDrawCommand, WindowDrawDetails};

pub use opengl::{build_context, build_window, Context as WindowedContext, GlWindow};
Expand Down Expand Up @@ -145,7 +147,7 @@ impl Renderer {
self.cursor_renderer.prepare_frame()
}

pub fn draw_frame(&mut self, root_canvas: &Canvas, dt: f32) {
pub fn draw_frame(&mut self, root_canvas: &Canvas, dt: f32, ime: &Ime, w: f32) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give the the w parameter a more descriptive name? We don’t use the shorthand for width anywhere else?

tracy_zone!("renderer_draw_frame");
let default_background = self.grid_renderer.get_default_background();
let font_dimensions = self.grid_renderer.font_dimensions;
Expand Down Expand Up @@ -195,7 +197,7 @@ impl Renderer {
.collect();

self.cursor_renderer
.draw(&mut self.grid_renderer, root_canvas);
.draw(&mut self.grid_renderer, root_canvas, ime.preedit(), w);

self.profiler.draw(root_canvas, dt);

Expand Down
11 changes: 7 additions & 4 deletions 11 src/window/keyboard_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,13 @@ impl KeyboardManager {
log::trace!("Ime commit {text}");
send_ui(SerialCommand::Keyboard(text.to_string()));
}
Event::WindowEvent {
event: WindowEvent::Ime(Ime::Preedit(text, cursor_offset)),
..
} => self.ime_preedit = (text.to_string(), *cursor_offset),
// Event::WindowEvent {
// event: WindowEvent::Ime(Ime::Preedit(text, cursor_offset)),
// ..
// } => {
// self.ime_preedit = (text.to_string(), *cursor_offset);
// log::trace!("Ime preedit {text}");
// }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to keep the ime state in the keyboard manager. And I also think that this breaks something, since we used to block keyboard inputs when the pre-edit was active, and that code is no longer effective.

Event::WindowEvent {
event: WindowEvent::ModifiersChanged(modifiers),
..
Expand Down
60 changes: 49 additions & 11 deletions 60 src/window/window_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ use crate::{
bridge::{send_ui, ParallelCommand, SerialCommand},
dimensions::Dimensions,
profiling::{tracy_frame, tracy_gpu_collect, tracy_gpu_zone, tracy_plot, tracy_zone},
renderer::{build_context, DrawCommand, GlWindow, Renderer, VSync, WindowedContext},
renderer::{
build_context,
ime::{Ime, Preedit},
DrawCommand, GlWindow, Renderer, VSync, WindowedContext,
},
running_tracker::RUNNING_TRACKER,
settings::{SettingsChanged, DEFAULT_GRID_SIZE, MIN_GRID_SIZE, SETTINGS},
window::{ShouldRender, WindowSize},
Expand Down Expand Up @@ -57,8 +61,8 @@ pub struct WinitWindowWrapper {
font_changed_last_frame: bool,
saved_inner_size: PhysicalSize<u32>,
saved_grid_size: Option<Dimensions>,
ime_enabled: bool,
ime_position: PhysicalPosition<i32>,
ime: Ime,
window_width: f32,
requested_columns: Option<u64>,
requested_lines: Option<u64>,
ui_state: UIState,
Expand Down Expand Up @@ -119,8 +123,8 @@ impl WinitWindowWrapper {
font_changed_last_frame: false,
saved_inner_size,
saved_grid_size: None,
ime_enabled,
ime_position: PhysicalPosition::new(-1, -1),
ime: Ime::new(),
window_width: 0.0,
requested_columns: None,
requested_lines: None,
ui_state: UIState::Initing,
Expand Down Expand Up @@ -158,7 +162,7 @@ impl WinitWindowWrapper {
}

pub fn set_ime(&mut self, ime_enabled: bool) {
self.ime_enabled = ime_enabled;
self.ime.set_enabled(ime_enabled);
self.windowed_context.window().set_ime_allowed(ime_enabled);
}

Expand Down Expand Up @@ -204,7 +208,7 @@ impl WinitWindowWrapper {
}
}
WindowSettingsChanged::InputIme(ime_enabled) => {
if self.ime_enabled != ime_enabled {
if self.ime.is_enabled() != ime_enabled {
self.set_ime(ime_enabled);
}
}
Expand Down Expand Up @@ -317,6 +321,32 @@ impl WinitWindowWrapper {
tracy_zone!("Moved");
self.vsync.update(&self.windowed_context);
}

Event::WindowEvent {
event: WindowEvent::Resized(size),
..
} => {
if size.width != 0 && size.height != 0 {
self.window_width = size.width as f32;
}
}
Event::WindowEvent {
event: WindowEvent::Ime(ime),
..
} => match ime {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clippy warnings should be fixed

winit::event::Ime::Preedit(text, cursor_offset) => {
let preedit = if text.is_empty() {
None
} else {
Some(Preedit::new(text, cursor_offset.map(|offset| offset.0)))
};

if self.ime.preedit() != preedit.as_ref() {
self.ime.set_preedit(preedit);
}
}
_ => {}
},
Event::UserEvent(UserEvent::DrawCommandBatch(batch)) => {
self.handle_draw_commands(batch);
}
Expand Down Expand Up @@ -352,7 +382,14 @@ impl WinitWindowWrapper {

pub fn draw_frame(&mut self, dt: f32) {
tracy_zone!("draw_frame");
self.renderer.draw_frame(self.skia_renderer.canvas(), dt);
self.renderer.prepare_lines();
self.renderer.draw_frame(
self.skia_renderer.canvas(),
dt,
&self.ime,
self.window_width,
);

{
tracy_gpu_zone!("skia flush");
self.skia_renderer.gr_context.flush_and_submit();
Expand Down Expand Up @@ -418,7 +455,7 @@ impl WinitWindowWrapper {
}

// Ensure that the window has the correct IME state
self.set_ime(self.ime_enabled);
self.set_ime(self.ime.is_enabled());
};
}

Expand Down Expand Up @@ -559,8 +596,9 @@ impl WinitWindowWrapper {
cursor_position.x.round() as i32,
cursor_position.y.round() as i32 + font_dimensions.height as i32,
);
if position != self.ime_position {
self.ime_position = position;
if position != self.ime.position() {
// self.ime.position = position;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commented code should be removed

self.ime.set_position(position);
self.windowed_context.window().set_ime_cursor_area(
Position::Physical(position),
PhysicalSize::new(100, font_dimensions.height as u32),
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.