Refactor and simplify input_method API
This commit is contained in:
parent
d5ee9c2795
commit
ae10adda74
19 changed files with 540 additions and 472 deletions
|
|
@ -141,6 +141,7 @@ pub fn window_event(
|
|||
scale_factor: f64,
|
||||
modifiers: winit::keyboard::ModifiersState,
|
||||
) -> Option<Event> {
|
||||
use winit::event::Ime;
|
||||
use winit::event::WindowEvent;
|
||||
|
||||
match event {
|
||||
|
|
@ -284,19 +285,15 @@ pub fn window_event(
|
|||
self::modifiers(new_modifiers.state()),
|
||||
)))
|
||||
}
|
||||
WindowEvent::Ime(ime) => {
|
||||
use winit::event::Ime;
|
||||
println!("ime event: {:?}", ime);
|
||||
Some(Event::InputMethod(match ime {
|
||||
Ime::Enabled => input_method::Event::Enabled,
|
||||
Ime::Preedit(s, size) => input_method::Event::Preedit(
|
||||
s,
|
||||
size.map(|(start, end)| (start..end)),
|
||||
),
|
||||
Ime::Commit(s) => input_method::Event::Commit(s),
|
||||
Ime::Disabled => input_method::Event::Disabled,
|
||||
}))
|
||||
}
|
||||
WindowEvent::Ime(event) => Some(Event::InputMethod(match event {
|
||||
Ime::Enabled => input_method::Event::Opened,
|
||||
Ime::Preedit(content, size) => input_method::Event::Preedit(
|
||||
content,
|
||||
size.map(|(start, end)| (start..end)),
|
||||
),
|
||||
Ime::Commit(content) => input_method::Event::Commit(content),
|
||||
Ime::Disabled => input_method::Event::Closed,
|
||||
})),
|
||||
WindowEvent::Focused(focused) => Some(Event::Window(if focused {
|
||||
window::Event::Focused
|
||||
} else {
|
||||
|
|
@ -1174,7 +1171,7 @@ pub fn resize_direction(
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts some [`window::Icon`] into it's `winit` counterpart.
|
||||
/// Converts some [`window::Icon`] into its `winit` counterpart.
|
||||
///
|
||||
/// Returns `None` if there is an error during the conversion.
|
||||
pub fn icon(icon: window::Icon) -> Option<winit::window::Icon> {
|
||||
|
|
@ -1183,6 +1180,17 @@ pub fn icon(icon: window::Icon) -> Option<winit::window::Icon> {
|
|||
winit::window::Icon::from_rgba(pixels, size.width, size.height).ok()
|
||||
}
|
||||
|
||||
/// Convertions some [`input_method::Purpose`] to its `winit` counterpart.
|
||||
pub fn ime_purpose(
|
||||
purpose: input_method::Purpose,
|
||||
) -> winit::window::ImePurpose {
|
||||
match purpose {
|
||||
input_method::Purpose::Normal => winit::window::ImePurpose::Normal,
|
||||
input_method::Purpose::Secure => winit::window::ImePurpose::Password,
|
||||
input_method::Purpose::Terminal => winit::window::ImePurpose::Terminal,
|
||||
}
|
||||
}
|
||||
|
||||
// See: https://en.wikipedia.org/wiki/Private_Use_Areas
|
||||
fn is_private_use(c: char) -> bool {
|
||||
('\u{E000}'..='\u{F8FF}').contains(&c)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ mod state;
|
|||
mod window_manager;
|
||||
|
||||
pub use state::State;
|
||||
use winit::dpi::LogicalPosition;
|
||||
use winit::dpi::LogicalSize;
|
||||
|
||||
use crate::conversion;
|
||||
use crate::core;
|
||||
|
|
@ -581,8 +579,6 @@ async fn run_instance<P, C>(
|
|||
let mut clipboard = Clipboard::unconnected();
|
||||
let mut compositor_receiver: Option<oneshot::Receiver<_>> = None;
|
||||
|
||||
let mut preedit = Preedit::<P>::new();
|
||||
|
||||
debug.startup_finished();
|
||||
|
||||
loop {
|
||||
|
|
@ -878,29 +874,15 @@ async fn run_instance<P, C>(
|
|||
|
||||
if let user_interface::State::Updated {
|
||||
redraw_request,
|
||||
caret_info,
|
||||
input_method,
|
||||
} = ui_state
|
||||
{
|
||||
match redraw_request {
|
||||
Some(window::RedrawRequest::NextFrame) => {
|
||||
window.raw.request_redraw();
|
||||
window.redraw_at = None;
|
||||
}
|
||||
Some(window::RedrawRequest::At(at)) => {
|
||||
window.redraw_at = Some(at);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
if let Some(caret_info) = caret_info {
|
||||
update_input_method(
|
||||
window,
|
||||
&mut preedit,
|
||||
&caret_info,
|
||||
);
|
||||
}
|
||||
window.request_redraw(redraw_request);
|
||||
window.request_input_method(input_method);
|
||||
}
|
||||
|
||||
window.draw_preedit();
|
||||
|
||||
debug.render_started();
|
||||
match compositor.present(
|
||||
&mut window.renderer,
|
||||
|
|
@ -1048,31 +1030,14 @@ async fn run_instance<P, C>(
|
|||
match ui_state {
|
||||
user_interface::State::Updated {
|
||||
redraw_request: _redraw_request,
|
||||
caret_info,
|
||||
input_method,
|
||||
} => {
|
||||
#[cfg(not(
|
||||
feature = "unconditional-rendering"
|
||||
))]
|
||||
match _redraw_request {
|
||||
Some(
|
||||
window::RedrawRequest::NextFrame,
|
||||
) => {
|
||||
window.raw.request_redraw();
|
||||
window.redraw_at = None;
|
||||
}
|
||||
Some(window::RedrawRequest::At(at)) => {
|
||||
window.redraw_at = Some(at);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
window.request_redraw(_redraw_request);
|
||||
|
||||
if let Some(caret_info) = caret_info {
|
||||
update_input_method(
|
||||
window,
|
||||
&mut preedit,
|
||||
&caret_info,
|
||||
);
|
||||
}
|
||||
window.request_input_method(input_method);
|
||||
}
|
||||
user_interface::State::Outdated => {
|
||||
uis_stale = true;
|
||||
|
|
@ -1165,111 +1130,6 @@ async fn run_instance<P, C>(
|
|||
let _ = ManuallyDrop::into_inner(user_interfaces);
|
||||
}
|
||||
|
||||
fn update_input_method<P, C>(
|
||||
window: &mut crate::program::window_manager::Window<P, C>,
|
||||
preedit: &mut Preedit<P>,
|
||||
caret_info: &crate::core::CaretInfo,
|
||||
) where
|
||||
P: Program,
|
||||
C: Compositor<Renderer = P::Renderer> + 'static,
|
||||
{
|
||||
window.raw.set_ime_allowed(caret_info.input_method_allowed);
|
||||
window.raw.set_ime_cursor_area(
|
||||
LogicalPosition::new(caret_info.position.x, caret_info.position.y),
|
||||
LogicalSize::new(10, 10),
|
||||
);
|
||||
|
||||
let text = window.state.preedit();
|
||||
if !text.is_empty() {
|
||||
preedit.update(text.as_str(), &window.renderer);
|
||||
preedit.fill(
|
||||
&mut window.renderer,
|
||||
window.state.text_color(),
|
||||
window.state.background_color(),
|
||||
caret_info.position,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
struct Preedit<P: Program> {
|
||||
content: Option<<P::Renderer as core::text::Renderer>::Paragraph>,
|
||||
}
|
||||
|
||||
impl<P: Program> Preedit<P> {
|
||||
fn new() -> Self {
|
||||
Self { content: None }
|
||||
}
|
||||
|
||||
fn update(&mut self, text: &str, renderer: &P::Renderer) {
|
||||
use core::text::Paragraph as _;
|
||||
use core::text::Renderer as _;
|
||||
|
||||
self.content = Some(
|
||||
<P::Renderer as core::text::Renderer>::Paragraph::with_text(
|
||||
core::Text::<&str, <P::Renderer as core::text::Renderer>::Font> {
|
||||
content: text,
|
||||
bounds: Size::INFINITY,
|
||||
size: renderer.default_size(),
|
||||
line_height: core::text::LineHeight::default(),
|
||||
font: renderer.default_font(),
|
||||
horizontal_alignment: core::alignment::Horizontal::Left,
|
||||
vertical_alignment: core::alignment::Vertical::Top, //Bottom,
|
||||
shaping: core::text::Shaping::Advanced,
|
||||
wrapping: core::text::Wrapping::None,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
fn fill(
|
||||
&self,
|
||||
renderer: &mut P::Renderer,
|
||||
fore_color: core::Color,
|
||||
bg_color: core::Color,
|
||||
caret_position: Point,
|
||||
) {
|
||||
use core::text::Paragraph as _;
|
||||
use core::text::Renderer as _;
|
||||
use core::Renderer as _;
|
||||
|
||||
let Some(ref content) = self.content else {
|
||||
return;
|
||||
};
|
||||
if content.min_width() < 1.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let top_left = Point::new(
|
||||
caret_position.x,
|
||||
caret_position.y - content.min_height(),
|
||||
);
|
||||
let bounds = core::Rectangle::new(top_left, content.min_bounds());
|
||||
renderer.with_layer(bounds, |renderer| {
|
||||
renderer.fill_quad(
|
||||
core::renderer::Quad {
|
||||
bounds,
|
||||
..Default::default()
|
||||
},
|
||||
core::Background::Color(bg_color),
|
||||
);
|
||||
|
||||
let underline = 2.;
|
||||
renderer.fill_quad(
|
||||
core::renderer::Quad {
|
||||
bounds: bounds.shrink(core::Padding {
|
||||
top: bounds.height - underline,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
core::Background::Color(fore_color),
|
||||
);
|
||||
|
||||
renderer.fill_paragraph(content, top_left, fore_color, bounds);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a window's [`UserInterface`] for the [`Program`].
|
||||
fn build_user_interface<'a, P: Program>(
|
||||
program: &'a P,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::core::{Color, Size};
|
|||
use crate::graphics::Viewport;
|
||||
use crate::program::Program;
|
||||
|
||||
use winit::event::{Ime, Touch, WindowEvent};
|
||||
use winit::event::{Touch, WindowEvent};
|
||||
use winit::window::Window;
|
||||
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
|
@ -22,7 +22,6 @@ where
|
|||
modifiers: winit::keyboard::ModifiersState,
|
||||
theme: P::Theme,
|
||||
style: theme::Style,
|
||||
preedit: String,
|
||||
}
|
||||
|
||||
impl<P: Program> Debug for State<P>
|
||||
|
|
@ -74,7 +73,6 @@ where
|
|||
modifiers: winit::keyboard::ModifiersState::default(),
|
||||
theme,
|
||||
style,
|
||||
preedit: String::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -138,11 +136,6 @@ where
|
|||
self.style.text_color
|
||||
}
|
||||
|
||||
/// TODO
|
||||
pub fn preedit(&self) -> String {
|
||||
self.preedit.clone()
|
||||
}
|
||||
|
||||
/// Processes the provided window event and updates the [`State`] accordingly.
|
||||
pub fn update(
|
||||
&mut self,
|
||||
|
|
@ -186,9 +179,6 @@ where
|
|||
WindowEvent::ModifiersChanged(new_modifiers) => {
|
||||
self.modifiers = new_modifiers.state();
|
||||
}
|
||||
WindowEvent::Ime(Ime::Preedit(text, _)) => {
|
||||
self.preedit = text.clone();
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
use crate::conversion;
|
||||
use crate::core::alignment;
|
||||
use crate::core::mouse;
|
||||
use crate::core::renderer;
|
||||
use crate::core::text;
|
||||
use crate::core::theme;
|
||||
use crate::core::time::Instant;
|
||||
use crate::core::window::Id;
|
||||
use crate::core::{Point, Size};
|
||||
use crate::core::window::{Id, RedrawRequest};
|
||||
use crate::core::{
|
||||
Color, InputMethod, Padding, Point, Rectangle, Size, Text, Vector,
|
||||
};
|
||||
use crate::graphics::Compositor;
|
||||
use crate::program::{Program, State};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
use winit::dpi::{LogicalPosition, LogicalSize};
|
||||
use winit::monitor::MonitorHandle;
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
|
|
@ -65,6 +72,7 @@ where
|
|||
renderer,
|
||||
mouse_interaction: mouse::Interaction::None,
|
||||
redraw_at: None,
|
||||
preedit: None,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -155,6 +163,7 @@ where
|
|||
pub surface: C::Surface,
|
||||
pub renderer: P::Renderer,
|
||||
pub redraw_at: Option<Instant>,
|
||||
preedit: Option<Preedit<P::Renderer>>,
|
||||
}
|
||||
|
||||
impl<P, C> Window<P, C>
|
||||
|
|
@ -179,4 +188,136 @@ where
|
|||
|
||||
Size::new(size.width, size.height)
|
||||
}
|
||||
|
||||
pub fn request_redraw(&mut self, redraw_request: RedrawRequest) {
|
||||
match redraw_request {
|
||||
RedrawRequest::NextFrame => {
|
||||
self.raw.request_redraw();
|
||||
self.redraw_at = None;
|
||||
}
|
||||
RedrawRequest::At(at) => {
|
||||
self.redraw_at = Some(at);
|
||||
}
|
||||
RedrawRequest::Wait => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_input_method(&mut self, input_method: InputMethod) {
|
||||
self.raw.set_ime_allowed(match input_method {
|
||||
InputMethod::Disabled => false,
|
||||
InputMethod::Allowed | InputMethod::Open { .. } => true,
|
||||
});
|
||||
|
||||
if let InputMethod::Open {
|
||||
position,
|
||||
purpose,
|
||||
preedit,
|
||||
} = input_method
|
||||
{
|
||||
self.raw.set_ime_cursor_area(
|
||||
LogicalPosition::new(position.x, position.y),
|
||||
LogicalSize::new(10, 10),
|
||||
);
|
||||
|
||||
self.raw.set_ime_purpose(conversion::ime_purpose(purpose));
|
||||
|
||||
if let Some(content) = preedit {
|
||||
if let Some(preedit) = &mut self.preedit {
|
||||
preedit.update(&content, &self.renderer);
|
||||
} else {
|
||||
let mut preedit = Preedit::new();
|
||||
preedit.update(&content, &self.renderer);
|
||||
|
||||
self.preedit = Some(preedit);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.preedit = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_preedit(&mut self) {
|
||||
if let Some(preedit) = &self.preedit {
|
||||
preedit.draw(
|
||||
&mut self.renderer,
|
||||
self.state.text_color(),
|
||||
self.state.background_color(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Preedit<Renderer>
|
||||
where
|
||||
Renderer: text::Renderer,
|
||||
{
|
||||
position: Point,
|
||||
content: text::paragraph::Plain<Renderer::Paragraph>,
|
||||
}
|
||||
|
||||
impl<Renderer> Preedit<Renderer>
|
||||
where
|
||||
Renderer: text::Renderer,
|
||||
{
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
position: Point::ORIGIN,
|
||||
content: text::paragraph::Plain::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, text: &str, renderer: &Renderer) {
|
||||
self.content.update(Text {
|
||||
content: text,
|
||||
bounds: Size::INFINITY,
|
||||
size: renderer.default_size(),
|
||||
line_height: text::LineHeight::default(),
|
||||
font: renderer.default_font(),
|
||||
horizontal_alignment: alignment::Horizontal::Left,
|
||||
vertical_alignment: alignment::Vertical::Top, //Bottom,
|
||||
shaping: text::Shaping::Advanced,
|
||||
wrapping: text::Wrapping::None,
|
||||
});
|
||||
}
|
||||
|
||||
fn draw(&self, renderer: &mut Renderer, color: Color, background: Color) {
|
||||
if self.content.min_width() < 1.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let top_left =
|
||||
self.position - Vector::new(0.0, self.content.min_height());
|
||||
|
||||
let bounds = Rectangle::new(top_left, self.content.min_bounds());
|
||||
|
||||
renderer.with_layer(bounds, |renderer| {
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
bounds,
|
||||
..Default::default()
|
||||
},
|
||||
background,
|
||||
);
|
||||
|
||||
renderer.fill_paragraph(
|
||||
self.content.raw(),
|
||||
top_left,
|
||||
color,
|
||||
bounds,
|
||||
);
|
||||
|
||||
const UNDERLINE: f32 = 2.0;
|
||||
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
bounds: bounds.shrink(Padding {
|
||||
top: bounds.height - UNDERLINE,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
color,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue