Skip to content

Commit 13eef3d

Browse files
committed
Render IME selected string in different color from other preedit string
1 parent 9b6329b commit 13eef3d

File tree

10 files changed

+221
-34
lines changed

10 files changed

+221
-34
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wezterm-gui/src/scripting/guiwin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ impl UserData for GuiWin {
153153
.notify(TermWindowNotif::Apply(Box::new(move |term_window| {
154154
tx.try_send(match term_window.composition_status() {
155155
DeadKeyStatus::None => None,
156-
DeadKeyStatus::Composing(s) => Some(s.clone()),
156+
DeadKeyStatus::Composing(s, ..) => Some(s.clone()),
157157
})
158158
.ok();
159159
})));

wezterm-gui/src/termwindow/render.rs

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,16 +1891,18 @@ impl super::TermWindow {
18911891
};
18921892

18931893
// Referencing the text being composed, but only if it belongs to this pane
1894-
let composing = if cursor_idx.is_some() {
1895-
if let DeadKeyStatus::Composing(composing) = &self.dead_key_status {
1896-
Some(composing)
1894+
let (composing, selected_range) = if cursor_idx.is_some() {
1895+
if let DeadKeyStatus::Composing(composing, selected_range) = &self.dead_key_status {
1896+
(Some(composing), selected_range.as_ref())
18971897
} else {
1898-
None
1898+
(None, None)
18991899
}
19001900
} else {
1901-
None
1901+
(None, None)
19021902
};
19031903

1904+
let mut selected_start = 0;
1905+
let mut selected_width = 0;
19041906
let mut composition_width = 0;
19051907

19061908
let (bidi_enabled, bidi_direction) = params.line.bidi_info();
@@ -1923,6 +1925,12 @@ impl super::TermWindow {
19231925
);
19241926
cell_clusters = line.cluster(bidi_hint);
19251927
composition_width = unicode_column_width(composing, None);
1928+
1929+
if let Some(selected_range) = selected_range {
1930+
selected_start = unicode_column_width(&composing[..selected_range.start], None);
1931+
selected_width = unicode_column_width(&composing[selected_range.clone()], None);
1932+
}
1933+
19261934
&cell_clusters
19271935
} else {
19281936
cell_clusters = params.line.cluster(bidi_hint);
@@ -1944,6 +1952,12 @@ impl super::TermWindow {
19441952
0..0
19451953
};
19461954

1955+
let selected_cursor_range = if selected_width > 0 {
1956+
params.cursor.x + selected_start..params.cursor.x + selected_start + selected_width
1957+
} else {
1958+
0..0
1959+
};
1960+
19471961
let cursor_range_pixels = params.left_pixel_x + cursor_range.start as f32 * cell_width
19481962
..params.left_pixel_x + cursor_range.end as f32 * cell_width;
19491963

@@ -2140,6 +2154,38 @@ impl super::TermWindow {
21402154

21412155
quad.set_fg_color(cursor_border_color);
21422156
quad.set_alt_color_and_mix_value(cursor_border_color_alt, cursor_border_mix);
2157+
2158+
if !selected_cursor_range.is_empty()
2159+
&& (cursor_range.start <= selected_cursor_range.start)
2160+
&& (selected_cursor_range.end <= cursor_range.end)
2161+
{
2162+
let mut quad = layers[0].allocate()?;
2163+
quad.set_position(
2164+
pos_x
2165+
+ (selected_cursor_range.start - cursor_range.start) as f32
2166+
* cell_width,
2167+
pos_y,
2168+
pos_x
2169+
+ (selected_cursor_range.end - cursor_range.start) as f32 * cell_width,
2170+
pos_y + cell_height,
2171+
);
2172+
quad.set_hsv(hsv);
2173+
quad.set_has_color(false);
2174+
2175+
quad.set_texture(
2176+
gl_state
2177+
.glyph_cache
2178+
.borrow_mut()
2179+
.cursor_sprite(
2180+
cursor_shape,
2181+
&params.render_metrics,
2182+
(selected_cursor_range.end - selected_cursor_range.start) as u8,
2183+
)?
2184+
.texture_coords(),
2185+
);
2186+
2187+
quad.set_fg_color(params.selection_bg);
2188+
}
21432189
}
21442190
}
21452191

window/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ smithay-client-toolkit = {version = "0.15", default-features=false, optional=tru
7676
wayland-protocols = {version="0.29", optional=true}
7777
wayland-client = {version="0.29", optional=true}
7878
wayland-egl = {version="0.29", optional=true}
79-
xcb-imdkit = { version="0.2", git="https://github.com/wez/xcb-imdkit-rs.git", rev="ede7c71b85fe2537efef6cf999a45690316211cf"}
79+
xcb-imdkit = {version="0.2", git="https://github.com/kumattau/xcb-imdkit-rs.git", rev="9ba1522493167c2430f20b06597240bd00bebd81"}
8080

8181
[target.'cfg(target_os="macos")'.dependencies]
8282
cocoa = "0.20"

window/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use bitflags::bitflags;
33
use config::{ConfigHandle, Dimension, GeometryOrigin};
44
use promise::Future;
55
use std::any::Any;
6+
use std::ops::Range;
67
use std::path::PathBuf;
78
use std::rc::Rc;
89
use thiserror::Error;
@@ -131,7 +132,7 @@ pub enum DeadKeyStatus {
131132
None,
132133
/// Holding until composition is done; the string is the uncommitted
133134
/// composition text to show as a placeholder
134-
Composing(String),
135+
Composing(String, Option<Range<usize>>),
135136
}
136137

137138
#[derive(Debug)]

window/src/os/macos/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@ fn nsstring(s: &str) -> StrongPtr {
1919
unsafe { StrongPtr::new(NSString::alloc(nil).init_str(s)) }
2020
}
2121

22-
unsafe fn nsstring_to_str<'a>(mut ns: *mut Object) -> &'a str {
22+
unsafe fn unattributed(mut ns: *mut Object) -> *mut Object {
2323
let is_astring: bool = msg_send![ns, isKindOfClass: class!(NSAttributedString)];
2424
if is_astring {
2525
ns = msg_send![ns, string];
2626
}
27+
ns
28+
}
29+
30+
unsafe fn nsstring_to_str<'a>(mut ns: *mut Object) -> &'a str {
31+
ns = unattributed(ns);
2732
let data = NSString::UTF8String(ns as id) as *const u8;
2833
let len = NSString::len(ns as id);
2934
let bytes = std::slice::from_raw_parts(data, len);

window/src/os/macos/window.rs

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#![allow(clippy::let_unit_value)]
33

44
use super::keycodes::*;
5-
use super::{nsstring, nsstring_to_str};
5+
use super::{nsstring, nsstring_to_str, unattributed};
66
use crate::connection::ConnectionOps;
77
use crate::parameters::{Border, Parameters, TitleBar};
88
use crate::{
@@ -41,6 +41,7 @@ use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
4141
use std::any::Any;
4242
use std::cell::RefCell;
4343
use std::ffi::c_void;
44+
use std::ops::Range;
4445
use std::path::PathBuf;
4546
use std::rc::Rc;
4647
use std::str::FromStr;
@@ -434,6 +435,7 @@ impl Window {
434435
ime_last_event: None,
435436
live_resizing: false,
436437
ime_text: String::new(),
438+
selected_range: None,
437439
}));
438440

439441
let window: id = msg_send![get_window_class(), alloc];
@@ -1154,6 +1156,7 @@ struct Inner {
11541156
live_resizing: bool,
11551157

11561158
ime_text: String,
1159+
selected_range: Option<Range<usize>>,
11571160
}
11581161

11591162
#[repr(C)]
@@ -1637,6 +1640,7 @@ impl WindowView {
16371640
if let Some(myself) = Self::get_this(this) {
16381641
let mut inner = myself.inner.borrow_mut();
16391642
inner.ime_text = s.to_string();
1643+
inner.selected_range = calc_str_selected_range(astring, selected_range, s.len());
16401644

16411645
/*
16421646
let key_is_down = inner.key_is_down.take().unwrap_or(true);
@@ -2095,7 +2099,7 @@ impl WindowView {
20952099
Ok(TranslateStatus::Composing(composing)) => {
20962100
// Next key press in dead key sequence is pending.
20972101
inner.events.dispatch(WindowEvent::AdviseDeadKeyStatus(
2098-
DeadKeyStatus::Composing(composing),
2102+
DeadKeyStatus::Composing(composing, None),
20992103
));
21002104

21012105
return;
@@ -2205,7 +2209,10 @@ impl WindowView {
22052209
// If it didn't generate an event, then a composition
22062210
// is pending.
22072211
let status = if inner.ime_last_event.is_none() {
2208-
DeadKeyStatus::Composing(inner.ime_text.clone())
2212+
DeadKeyStatus::Composing(
2213+
inner.ime_text.clone(),
2214+
inner.selected_range.clone(),
2215+
)
22092216
} else {
22102217
DeadKeyStatus::None
22112218
};
@@ -2235,7 +2242,10 @@ impl WindowView {
22352242
let status = if inner.ime_text.is_empty() {
22362243
DeadKeyStatus::None
22372244
} else {
2238-
DeadKeyStatus::Composing(inner.ime_text.clone())
2245+
DeadKeyStatus::Composing(
2246+
inner.ime_text.clone(),
2247+
inner.selected_range.clone(),
2248+
)
22392249
};
22402250
inner
22412251
.events
@@ -2870,3 +2880,48 @@ fn resolve_geom(geometry: RequestedWindowGeometry) -> ResolvedGeometry {
28702880

28712881
ResolvedGeometry { pos, width, height }
28722882
}
2883+
2884+
fn calc_str_selected_range(
2885+
astring: *mut Object,
2886+
selected_range: NSRange,
2887+
max: usize,
2888+
) -> Option<Range<usize>> {
2889+
unsafe fn sub_tostr_len(ns: *mut Object, range: NSRange, max: usize) -> usize {
2890+
let sub = msg_send![ns, substringWithRange: range];
2891+
let len = nsstring_to_str(sub).len();
2892+
std::cmp::min(len, max)
2893+
}
2894+
2895+
let ns;
2896+
let ns_length;
2897+
unsafe {
2898+
ns = unattributed(astring);
2899+
ns_length = msg_send![ns, length];
2900+
}
2901+
2902+
let a_start = selected_range.0.location;
2903+
let a_end = a_start + selected_range.0.length;
2904+
if ns_length < a_end {
2905+
return None;
2906+
}
2907+
2908+
let s_start;
2909+
let s_end;
2910+
unsafe {
2911+
s_start = if 0 < a_start {
2912+
sub_tostr_len(ns, NSRange::new(0, a_start), max)
2913+
} else {
2914+
0
2915+
};
2916+
s_end = if a_end < ns_length {
2917+
s_start + sub_tostr_len(ns, NSRange::new(a_start, a_end - a_start), max)
2918+
} else {
2919+
max
2920+
};
2921+
}
2922+
if s_end <= s_start {
2923+
return None;
2924+
}
2925+
2926+
Some(s_start..s_end)
2927+
}

0 commit comments

Comments
 (0)