Skip to content

Commit 022a905

Browse files
authored
feat: support ime (#34)
* feat: support ime * feat: support ime * feat: support ime
1 parent a38f95e commit 022a905

File tree

4 files changed

+62
-49
lines changed

4 files changed

+62
-49
lines changed

crates/egui-term/src/display/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ impl TerminalView<'_> {
4646
.term_ctx
4747
.to_range()
4848
.is_some_and(|r| r.contains(indexed.point));
49-
let is_hovered_hyperlink =
50-
self.term_ctx.hovered_hyperlink.as_ref().is_some_and(|r| {
51-
r.contains(&indexed.point) && r.contains(&state.mouse_position)
52-
});
49+
let is_hovered_hyperlink = self
50+
.term_ctx
51+
.hovered_hyperlink
52+
.as_ref()
53+
.is_some_and(|r| r.contains(&indexed.point) && r.contains(&state.mouse_point));
5354
let is_text_cell = indexed.c != ' ' && indexed.c != '\t';
5455

5556
let x = layout_min.x + indexed.point.column.saturating_mul(cell_width as usize) as f32;
@@ -112,6 +113,7 @@ impl TerminalView<'_> {
112113
cell_width / 2.
113114
};
114115

116+
state.cursor_position = Some(Pos2::new(x, y));
115117
shapes.push(Shape::Rect(RectShape::filled(
116118
Rect::from_min_size(Pos2::new(x, y), Vec2::new(cursor_width, cell_height)),
117119
CornerRadius::default(),

crates/egui-term/src/input/mod.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{BindingAction, InputKind, TerminalView};
44
use alacritty_terminal::grid::Dimensions;
55
use alacritty_terminal::selection::SelectionType;
66
use alacritty_terminal::term::TermMode;
7-
use egui::{Key, Modifiers, MouseWheelUnit, PointerButton, Pos2, Response, Vec2};
7+
use egui::{Key, Modifiers, MouseWheelUnit, PointerButton, Pos2, Rect, Response, Vec2};
88
use std::cmp::min;
99

1010
/// Minimum number of pixels at the bottom/top where selection scrolling is performed.
@@ -134,7 +134,7 @@ impl TerminalView<'_> {
134134
self.left_button_click(state, layout, position, modifiers, pressed)
135135
}
136136
PointerButton::Secondary => {
137-
state.cursor_position = Some(position);
137+
state.context_menu_position = Some(position);
138138
None
139139
}
140140
_ => None,
@@ -149,18 +149,18 @@ impl TerminalView<'_> {
149149
modifiers: &Modifiers,
150150
pressed: bool,
151151
) -> Option<InputAction> {
152-
if state.cursor_position.is_some() {
152+
if state.context_menu_position.is_some() {
153153
return None;
154154
}
155155
let terminal_mode = self.term_ctx.terminal.mode();
156156
if terminal_mode.intersects(TermMode::MOUSE_MODE) {
157157
Some(InputAction::BackendCall(BackendCommand::MouseReport(
158158
MouseButton::LeftButton,
159159
*modifiers,
160-
state.mouse_position,
160+
state.mouse_point,
161161
pressed,
162162
)))
163-
} else if pressed {
163+
} else if pressed && is_in_terminal(position, layout.rect) {
164164
state.is_dragged = true;
165165
Some(InputAction::BackendCall(start_select_command(
166166
layout, position,
@@ -189,7 +189,7 @@ impl TerminalView<'_> {
189189
*self.term_ctx.terminal.mode(),
190190
) {
191191
Some(BindingAction::LinkOpen) => Some(InputAction::BackendCall(
192-
BackendCommand::ProcessLink(LinkAction::Open, state.mouse_position),
192+
BackendCommand::ProcessLink(LinkAction::Open, state.mouse_point),
193193
)),
194194
_ => None,
195195
}
@@ -203,21 +203,22 @@ impl TerminalView<'_> {
203203
position: Pos2,
204204
modifiers: &Modifiers,
205205
) -> Vec<InputAction> {
206-
let cursor_x = position.x - layout.rect.min.x;
207-
let cursor_y = position.y - layout.rect.min.y;
206+
let mouse_x = position.x - layout.rect.min.x;
207+
let mouse_y = position.y - layout.rect.min.y;
208208

209-
state.mouse_position = selection_point(
210-
cursor_x,
211-
cursor_y,
209+
state.mouse_point = selection_point(
210+
mouse_x,
211+
mouse_y,
212212
self.term_ctx.size,
213213
self.term_ctx.terminal.grid().display_offset(),
214214
);
215+
state.mouse_position = Some(position);
215216

216217
let mut actions = vec![];
217218
// Handle command or selection update based on terminal mode and modifiers
218219
if state.is_dragged {
219220
if !self.term_ctx.selection_is_empty() {
220-
if let Some(action) = self.update_selection_scrolling(cursor_y as i32) {
221+
if let Some(action) = self.update_selection_scrolling(mouse_y as i32) {
221222
actions.push(action);
222223
}
223224
}
@@ -232,11 +233,11 @@ impl TerminalView<'_> {
232233
InputAction::BackendCall(BackendCommand::MouseReport(
233234
MouseButton::LeftMove,
234235
*modifiers,
235-
state.mouse_position,
236+
state.mouse_point,
236237
true,
237238
))
238239
} else {
239-
InputAction::BackendCall(BackendCommand::SelectUpdate(cursor_x, cursor_y))
240+
InputAction::BackendCall(BackendCommand::SelectUpdate(mouse_x, mouse_y))
240241
};
241242

242243
actions.push(cmd);
@@ -245,7 +246,7 @@ impl TerminalView<'_> {
245246
// Handle link hover if applicable
246247
actions.push(InputAction::BackendCall(BackendCommand::ProcessLink(
247248
LinkAction::Hover,
248-
state.mouse_position,
249+
state.mouse_point,
249250
)));
250251

251252
actions
@@ -293,3 +294,7 @@ fn start_select_command(layout: &Response, cursor_position: Pos2) -> BackendComm
293294
cursor_position.y - layout.rect.min.y,
294295
)
295296
}
297+
298+
pub fn is_in_terminal(pos: Pos2, rect: Rect) -> bool {
299+
pos.x > rect.min.x && pos.x < rect.max.x && pos.y > rect.min.y && pos.y < rect.max.y
300+
}

crates/egui-term/src/view.rs

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,30 @@ use crate::alacritty::{BackendCommand, TerminalContext};
22
use crate::bindings::Binding;
33
use crate::bindings::{BindingAction, Bindings, InputKind};
44
use crate::font::TerminalFont;
5-
use crate::input::InputAction;
5+
use crate::input::{is_in_terminal, InputAction};
66
use crate::scroll_bar::{InteractiveScrollbar, ScrollbarState};
77
use crate::theme::TerminalTheme;
88
use crate::types::Size;
99
use alacritty_terminal::grid::{Dimensions, Scroll};
1010
use alacritty_terminal::index::Point;
1111
use alacritty_terminal::vte::ansi::{Color, NamedColor};
12-
use egui::ImeEvent;
12+
use egui::output::IMEOutput;
1313
use egui::Widget;
1414
use egui::{Context, Event};
1515
use egui::{CursorIcon, Key};
1616
use egui::{Id, Pos2};
17+
use egui::{ImeEvent, Rect};
1718
use egui::{Response, Vec2};
1819

1920
#[derive(Clone, Default)]
2021
pub struct TerminalViewState {
2122
pub is_dragged: bool,
2223
pub scroll_pixels: f32,
2324
// for terminal
24-
pub mouse_position: Point,
25+
pub mouse_point: Point,
26+
pub mouse_position: Option<Pos2>,
27+
pub context_menu_position: Option<Pos2>,
2528
pub cursor_position: Option<Pos2>,
26-
// ime_enabled: bool,
27-
// ime_cursor_range: CursorRange,
2829
pub scrollbar_state: ScrollbarState,
2930
}
3031

@@ -80,13 +81,14 @@ impl Widget for TerminalView<'_> {
8081
}
8182

8283
// context menu
83-
if let Some(pos) = state.cursor_position {
84-
if !out_of_terminal(pos, &layout) {
84+
if let Some(pos) = state.context_menu_position {
85+
if is_in_terminal(pos, layout.rect) {
8586
self.context_menu(pos, &layout, ui);
8687
}
8788
}
89+
8890
if ui.input(|input_state| input_state.pointer.primary_clicked()) {
89-
state.cursor_position = None;
91+
state.context_menu_position = None;
9092
ui.close();
9193
}
9294

@@ -97,6 +99,20 @@ impl Widget for TerminalView<'_> {
9799
.resize(&layout)
98100
.process_input(&mut state, &layout);
99101

102+
if let Some(pos) = state.mouse_position {
103+
if is_in_terminal(pos, layout.rect) {
104+
if let Some(cur_pos) = state.cursor_position {
105+
ui.ctx().output_mut(|output| {
106+
let vec = Vec2::new(15., 15.);
107+
output.ime = Some(IMEOutput {
108+
rect: Rect::from_min_size(cur_pos, vec),
109+
cursor_rect: Rect::from_min_size(cur_pos, vec),
110+
})
111+
});
112+
}
113+
}
114+
}
115+
100116
let grid = term.term_ctx.terminal.grid_mut();
101117
let total_lines = grid.total_lines() as f32;
102118
let display_offset = grid.display_offset() as f32;
@@ -241,10 +257,10 @@ impl<'a> TerminalView<'a> {
241257
modifiers,
242258
pos,
243259
} => {
244-
let new_pos = if out_of_terminal(pos, layout) {
245-
pos.clamp(layout.rect.min, layout.rect.max)
246-
} else {
260+
let new_pos = if is_in_terminal(pos, layout.rect) {
247261
pos
262+
} else {
263+
pos.clamp(layout.rect.min, layout.rect.max)
248264
};
249265

250266
if let Some(action) =
@@ -257,15 +273,17 @@ impl<'a> TerminalView<'a> {
257273
input_actions = self.mouse_move(state, layout, pos, &modifiers)
258274
}
259275
Event::Ime(event) => match event {
260-
ImeEvent::Disabled => {
261-
// state.ime_enabled = false;
276+
ImeEvent::Preedit(text_mark) => {
277+
if text_mark != "\n" && text_mark != "\r" {
278+
// TODO: handle preedit
279+
}
262280
}
263-
ImeEvent::Enabled | ImeEvent::Preedit(_) => {
264-
// state.ime_enabled = true;
265-
}
266-
ImeEvent::Commit(text) => {
267-
input_actions.push(self.text_input(&text));
281+
ImeEvent::Commit(prediction) => {
282+
if prediction != "\n" && prediction != "\r" {
283+
input_actions.push(self.text_input(&prediction));
284+
}
268285
}
286+
_ => {}
269287
},
270288
_ => {}
271289
};
@@ -285,10 +303,3 @@ impl<'a> TerminalView<'a> {
285303
self
286304
}
287305
}
288-
289-
fn out_of_terminal(pos: Pos2, layout: &Response) -> bool {
290-
!(pos.x > layout.rect.min.x
291-
&& pos.x < layout.rect.max.x
292-
&& pos.y > layout.rect.min.y
293-
&& pos.y < layout.rect.max.y)
294-
}

nxshell/src/app.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,6 @@ impl eframe::App for NxShell {
126126
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
127127
ui.label("Sessions");
128128
});
129-
130-
// TODO: add close menu
131-
// ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
132-
// ui.label("Sessions");
133-
// });
134129
});
135130

136131
self.search_sessions(ui);

0 commit comments

Comments
 (0)