Skip to content
Merged
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
4 changes: 2 additions & 2 deletions iced_examples/counter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ impl Application for Counter {

fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::IcedEvent(event) => {
println!("hello {event:?}");
Message::IcedEvent(_event) => {
//println!("hello {event:?}");
Command::none()
}
Message::IncrementPressed => {
Expand Down
5 changes: 4 additions & 1 deletion iced_layershell/src/actions.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::reexport::{Anchor, Layer, WlRegion};
use iced::window::Id as IcedId;
use iced_core::input_method;
use iced_core::mouse::Interaction;
use layershellev::NewLayerShellSettings;
use layershellev::id::Id as LayerId;
Expand All @@ -15,7 +16,9 @@ pub(crate) enum LayerShellAction {
CustomActionsWithId(LayershellCustomActionsWithIdInner),
RedrawAll,
RedrawWindow(LayerId), // maybe one day it is useful, but now useless
NewMenu((IcedNewPopupSettings, iced_core::window::Id)),
NewMenu(IcedNewPopupSettings, iced_core::window::Id),
Ime(input_method::InputMethod),
ImeWithId(LayerId, input_method::InputMethod)
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
Expand Down
137 changes: 134 additions & 3 deletions iced_layershell/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ use crate::{
clipboard::LayerShellClipboard,
conversion,
error::Error,
ime_preedit::Preedit,
settings::VirtualKeyboardSettings,
};

use super::{Appearance, DefaultStyle};
use iced_graphics::{Compositor, compositor};
use state::State;

use iced_core::{Event as IcedCoreEvent, Size, time::Instant, window as IcedCoreWindow};
use iced_core::{
Event as IcedCoreEvent, InputMethod, Size, input_method, time::Instant,
window as IcedCoreWindow,
};

use iced_runtime::{Action, Debug, Program, UserInterface, task::Task, user_interface};

Expand Down Expand Up @@ -333,6 +337,25 @@ where
LayerShellAction::RedrawWindow(index) => {
ev.append_return_data(ReturnData::RedrawIndexRequest(index));
}
LayerShellAction::Ime(ime) => match ime {
iced_core::InputMethod::Disabled => {
ev.set_ime_allowed(false);
}
iced_core::InputMethod::Enabled {
position, purpose, ..
} => {
ev.set_ime_allowed(true);
ev.set_ime_purpose(conversion::ime_purpose(purpose));
ev.set_ime_cursor_area(
layershellev::dpi::LogicalPosition::new(position.x, position.y),
layershellev::dpi::LogicalSize {
width: 10,
height: 10,
},
ev.main_window().id(),
);
}
},
_ => {}
}
}
Expand All @@ -341,6 +364,94 @@ where
Ok(())
}

struct IMDrawer<A>
where
A: Application,
A::Theme: iced_core::theme::Base,
{
preedit: Option<Preedit<A::Renderer>>,
ime_state: Option<(iced_core::Point, input_method::Purpose)>,
}

impl<A> IMDrawer<A>
where
A: Application,
A::Theme: iced_core::theme::Base,
{
fn new() -> Self {
Self {
preedit: None,
ime_state: None,
}
}
pub fn request_input_method(
&mut self,
background_color: iced_core::Color,
input_method: InputMethod,
renderer: &A::Renderer,
) {
match input_method {
InputMethod::Disabled => {
self.disable_ime();
}
InputMethod::Enabled {
position,
purpose,
preedit,
} => {
self.enable_ime(position, purpose);

if let Some(preedit) = preedit {
if preedit.content.is_empty() {
self.preedit = None;
} else {
let mut overlay = self.preedit.take().unwrap_or_else(Preedit::new);

overlay.update(position, &preedit, background_color, renderer);

self.preedit = Some(overlay);
}
} else {
self.preedit = None;
}
}
}
}

pub fn draw_preedit(
&mut self,
renderer: &mut A::Renderer,
text_color: iced_core::Color,
background_color: iced_core::Color,
logical_size: iced_core::Size,
) {
use iced_core::Point;
use iced_core::Rectangle;
if let Some(preedit) = &self.preedit {
preedit.draw(
renderer,
text_color,
background_color,
&Rectangle::new(Point::ORIGIN, logical_size),
);
}
}

fn enable_ime(&mut self, position: iced_core::Point, purpose: input_method::Purpose) {
if self.ime_state != Some((position, purpose)) {
self.ime_state = Some((position, purpose));
}
}

fn disable_ime(&mut self) {
if self.ime_state.is_some() {
self.ime_state = None;
}

self.preedit = None;
}
}

#[allow(clippy::too_many_arguments)]
async fn run_instance<A, E, C>(
mut application: A,
Expand Down Expand Up @@ -370,6 +481,7 @@ async fn run_instance<A, E, C>(
}

let mut renderer = compositor.create_renderer();
let mut im_drawer: IMDrawer<A> = IMDrawer::new();

let cache = user_interface::Cache::default();

Expand Down Expand Up @@ -435,14 +547,33 @@ async fn run_instance<A, E, C>(
let redraw_event =
IcedCoreEvent::Window(IcedCoreWindow::Event::RedrawRequested(Instant::now()));

user_interface.update(
let (ui_state, _) = user_interface.update(
&[redraw_event.clone()],
state.cursor(),
&mut renderer,
&mut clipboard,
&mut messages,
);
events.push(redraw_event.clone());
if let user_interface::State::Updated {
redraw_request: _, // NOTE: I do not know how to use it now
input_method,
} = ui_state
{
events.push(redraw_event.clone());
custom_actions.push(LayerShellAction::Ime(input_method.clone()));
im_drawer.request_input_method(
state.background_color(),
input_method,
&renderer,
);
}
im_drawer.draw_preedit(
&mut renderer,
state.text_color(),
state.background_color(),
state.viewport().logical_size(),
);

runtime.broadcast(iced_futures::subscription::Event::Interaction {
window: main_id,
event: redraw_event,
Expand Down
17 changes: 17 additions & 0 deletions iced_layershell/src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::event::IcedButtonState;
use crate::event::WindowEvent as LayerShellEvent;
use iced::touch;
use iced_core::SmolStr;
use iced_core::input_method;
use iced_core::{Event as IcedEvent, keyboard, mouse};
use keymap::{key, physical_key};
use layershellev::keyboard::KeyLocation;
Expand Down Expand Up @@ -121,10 +122,26 @@ pub fn window_event(layerevent: &LayerShellEvent, modifiers: ModifiersState) ->
)),
LayerShellEvent::Unfocus => Some(IcedEvent::Window(iced::window::Event::Unfocused)),
LayerShellEvent::Focused => Some(IcedEvent::Window(iced::window::Event::Focused)),
LayerShellEvent::Ime(event) => Some(IcedEvent::InputMethod(match event {
layershellev::Ime::Enabled => input_method::Event::Opened,
layershellev::Ime::Preedit(content, size) => {
input_method::Event::Preedit(content.clone(), size.map(|(start, end)| (start..end)))
}
layershellev::Ime::Commit(content) => input_method::Event::Commit(content.clone()),
layershellev::Ime::Disabled => input_method::Event::Closed,
})),
_ => None,
}
}

pub fn ime_purpose(purpose: input_method::Purpose) -> layershellev::ImePurpose {
match purpose {
input_method::Purpose::Normal => layershellev::ImePurpose::Normal,
input_method::Purpose::Secure => layershellev::ImePurpose::Password,
input_method::Purpose::Terminal => layershellev::ImePurpose::Terminal,
}
}

pub(crate) fn mouse_interaction(interaction: mouse::Interaction) -> String {
use layershellev::reexport::wp_cursor_shape_device_v1::{Shape, ShapeName};
use mouse::Interaction;
Expand Down
2 changes: 2 additions & 0 deletions iced_layershell/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ pub enum WindowEvent {
x: f64,
y: f64,
},
Ime(layershellev::Ime),
}

#[derive(Debug)]
Expand Down Expand Up @@ -212,6 +213,7 @@ impl<Message: 'static> From<&DispatchMessage> for IcedLayerEvent<Message> {
DispatchMessage::ModifiersChanged(modifiers) => {
IcedLayerEvent::Window(WindowEvent::ModifiersChanged(*modifiers))
}
DispatchMessage::Ime(ime) => IcedLayerEvent::Window(WindowEvent::Ime(ime.clone())),
DispatchMessage::Axis {
horizontal,
vertical,
Expand Down
131 changes: 131 additions & 0 deletions iced_layershell/src/ime_preedit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use iced_core::{
Color, Padding, Point, Rectangle, Size, Text, Vector, alignment, input_method, renderer, text,
};

pub struct Preedit<Renderer>
where
Renderer: text::Renderer,
{
position: Point,
content: Renderer::Paragraph,
spans: Vec<text::Span<'static, (), Renderer::Font>>,
}

impl<Renderer> Preedit<Renderer>
where
Renderer: text::Renderer,
{
pub fn new() -> Self {
Self {
position: Point::ORIGIN,
spans: Vec::new(),
content: Renderer::Paragraph::default(),
}
}

pub fn update(
&mut self,
position: Point,
preedit: &input_method::Preedit,
background: Color,
renderer: &Renderer,
) {
self.position = position;

let spans = match &preedit.selection {
Some(selection) => {
vec![
text::Span::new(&preedit.content[..selection.start]),
text::Span::new(if selection.start == selection.end {
"\u{200A}"
} else {
&preedit.content[selection.start..selection.end]
})
.color(background),
text::Span::new(&preedit.content[selection.end..]),
]
}
_ => vec![text::Span::new(&preedit.content)],
};

if spans != self.spans.as_slice() {
use text::Paragraph as _;

self.content = Renderer::Paragraph::with_spans(Text {
content: &spans,
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,
shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::None,
});
}
}

pub fn draw(
&self,
renderer: &mut Renderer,
color: Color,
background: Color,
viewport: &Rectangle,
) {
use text::Paragraph as _;

if self.content.min_width() < 1.0 {
return;
}

let mut bounds = Rectangle::new(
self.position - Vector::new(0.0, self.content.min_height()),
self.content.min_bounds(),
);

bounds.x = bounds
.x
.max(viewport.x)
.min(viewport.x + viewport.width - bounds.width);

bounds.y = bounds
.y
.max(viewport.y)
.min(viewport.y + viewport.height - bounds.height);

renderer.with_layer(bounds, |renderer| {
renderer.fill_quad(
renderer::Quad {
bounds,
..Default::default()
},
background,
);

renderer.fill_paragraph(&self.content, bounds.position(), 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,
);

for span_bounds in self.content.span_bounds(1) {
renderer.fill_quad(
renderer::Quad {
bounds: span_bounds + (bounds.position() - Point::ORIGIN),
..Default::default()
},
color,
);
}
});
}
}
Loading