chore(text_input): add winit example and do some cleanup

This commit is contained in:
Ashley Wulber 2023-08-21 18:58:36 -04:00
parent 878c2dcd5d
commit cc74c63d9b
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
6 changed files with 56 additions and 32 deletions

View file

@ -124,5 +124,3 @@ exclude = [
[patch."https://github.com/pop-os/libcosmic"] [patch."https://github.com/pop-os/libcosmic"]
libcosmic = { path = "./", features = ["wayland", "tokio", "a11y"]} libcosmic = { path = "./", features = ["wayland", "tokio", "a11y"]}
[patch."https://github.com/pop-os/cosmic-time"]
cosmic-time = { path = "../cosmic-time"}

View file

@ -504,6 +504,10 @@ impl State {
.size(20) .size(20)
.id(INPUT_ID.clone()) .id(INPUT_ID.clone())
.into(), .into(),
cosmic::widget::text_input("test", &self.entry_value)
.width(Length::Fill)
.on_input(Message::InputChanged)
.into(),
]) ])
.into() .into()
} }

View file

@ -70,9 +70,9 @@ pub use warning::*;
pub mod cosmic_container; pub mod cosmic_container;
pub use cosmic_container::*; pub use cosmic_container::*;
#[cfg(feature = "wayland")] // #[cfg(feature = "wayland")]
pub mod text_input; pub mod text_input;
#[cfg(feature = "wayland")] // #[cfg(feature = "wayland")]
pub use text_input::*; pub use text_input::*;
/// An element to distinguish a boundary between two elements. /// An element to distinguish a boundary between two elements.

View file

@ -38,33 +38,27 @@ impl<'a> Editor<'a> {
} }
pub fn backspace(&mut self) { pub fn backspace(&mut self) {
match self.cursor.selection(self.value) { if let Some((start, end)) = self.cursor.selection(self.value) {
Some((start, end)) => { self.cursor.move_left(self.value);
self.cursor.move_left(self.value); self.value.remove_many(start, end);
self.value.remove_many(start, end); } else {
} let start = self.cursor.start(self.value);
None => {
let start = self.cursor.start(self.value);
if start > 0 { if start > 0 {
self.cursor.move_left(self.value); self.cursor.move_left(self.value);
self.value.remove(start - 1); self.value.remove(start - 1);
}
} }
} }
} }
pub fn delete(&mut self) { pub fn delete(&mut self) {
match self.cursor.selection(self.value) { if self.cursor.selection(self.value).is_some() {
Some(_) => { self.backspace();
self.backspace(); } else {
} let end = self.cursor.end(self.value);
None => {
let end = self.cursor.end(self.value);
if end < self.value.len() { if end < self.value.len() {
self.value.remove(end); self.value.remove(end);
}
} }
} }
} }

View file

@ -1,7 +1,6 @@
//! Display fields that can be filled with text. //! Display fields that can be filled with text.
//! //!
//! A [`TextInput`] has some local [`State`]. //! A [`TextInput`] has some local [`State`].
use crate::app;
use crate::theme::THEME; use crate::theme::THEME;
use super::cursor; use super::cursor;
@ -28,12 +27,16 @@ use iced_core::{
Clipboard, Color, Element, Layout, Length, Padding, Pixels, Point, Rectangle, Shell, Size, Clipboard, Color, Element, Layout, Length, Padding, Pixels, Point, Rectangle, Shell, Size,
Vector, Widget, Vector, Widget,
}; };
#[cfg(feature = "wayland")]
use iced_renderer::core::event::{wayland, PlatformSpecific}; use iced_renderer::core::event::{wayland, PlatformSpecific};
use iced_renderer::core::widget::OperationOutputWrapper; use iced_renderer::core::widget::OperationOutputWrapper;
#[cfg(feature = "wayland")]
use iced_runtime::command::platform_specific;
use iced_runtime::Command; use iced_runtime::Command;
use iced_runtime::command::platform_specific; #[cfg(feature = "wayland")]
use iced_runtime::command::platform_specific::wayland::data_device::{DataFromMimeType, DndIcon}; use iced_runtime::command::platform_specific::wayland::data_device::{DataFromMimeType, DndIcon};
#[cfg(feature = "wayland")]
use sctk::reexports::client::protocol::wl_data_device_manager::DndAction; use sctk::reexports::client::protocol::wl_data_device_manager::DndAction;
/// Creates a new [`TextInput`]. /// Creates a new [`TextInput`].
@ -140,6 +143,7 @@ where
.padding([spacing, spacing, spacing, spacing]) .padding([spacing, spacing, spacing, spacing])
} }
#[cfg(feature = "wayland")]
const SUPPORTED_MIME_TYPES: &[&str; 6] = &[ const SUPPORTED_MIME_TYPES: &[&str; 6] = &[
"text/plain;charset=utf-8", "text/plain;charset=utf-8",
"text/plain;charset=UTF-8", "text/plain;charset=UTF-8",
@ -148,8 +152,11 @@ const SUPPORTED_MIME_TYPES: &[&str; 6] = &[
"text/plain", "text/plain",
"TEXT", "TEXT",
]; ];
#[cfg(feature = "wayland")]
pub type DnDCommand = pub type DnDCommand =
Box<dyn Send + Sync + Fn() -> platform_specific::wayland::data_device::ActionInner>; Box<dyn Send + Sync + Fn() -> platform_specific::wayland::data_device::ActionInner>;
#[cfg(not(feature = "wayland"))]
pub type DnDCommand = ();
/// A field that can be filled with text. /// A field that can be filled with text.
/// ///
@ -356,6 +363,7 @@ where
/// [`Value`] if provided. /// [`Value`] if provided.
/// ///
/// [`Renderer`]: text::Renderer /// [`Renderer`]: text::Renderer
#[allow(clippy::too_many_arguments)]
pub fn draw( pub fn draw(
&self, &self,
tree: &Tree, tree: &Tree,
@ -394,6 +402,7 @@ where
} }
/// Sets the start dnd handler of the [`TextInput`]. /// Sets the start dnd handler of the [`TextInput`].
#[cfg(feature = "wayland")]
pub fn on_start_dnd(mut self, on_start_dnd: impl Fn(State) -> Message + 'a) -> Self { pub fn on_start_dnd(mut self, on_start_dnd: impl Fn(State) -> Message + 'a) -> Self {
self.on_create_dnd_source = Some(Box::new(on_start_dnd)); self.on_create_dnd_source = Some(Box::new(on_start_dnd));
self self
@ -401,6 +410,7 @@ where
/// Sets the dnd command produced handler of the [`TextInput`]. /// Sets the dnd command produced handler of the [`TextInput`].
/// Commands should be returned in the update function of the application. /// Commands should be returned in the update function of the application.
#[cfg(feature = "wayland")]
pub fn on_dnd_command_produced( pub fn on_dnd_command_produced(
mut self, mut self,
on_dnd_command_produced: impl Fn( on_dnd_command_produced: impl Fn(
@ -958,6 +968,7 @@ where
click.kind(), click.kind(),
state.cursor().state(value), state.cursor().state(value),
) { ) {
#[cfg(feature = "wayland")]
(None, click::Kind::Single, cursor::State::Selection { start, end }) => { (None, click::Kind::Single, cursor::State::Selection { start, end }) => {
// if something is already selected, we can start a drag and drop for a // if something is already selected, we can start a drag and drop for a
// single click that is on top of the selected text // single click that is on top of the selected text
@ -965,6 +976,7 @@ where
if is_secure { if is_secure {
return event::Status::Ignored; return event::Status::Ignored;
} }
if let ( if let (
Some(on_start_dnd), Some(on_start_dnd),
Some(on_dnd_command_produced), Some(on_dnd_command_produced),
@ -1384,6 +1396,7 @@ where
)); ));
} }
} }
#[cfg(feature = "wayland")]
Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DataSource( Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DataSource(
wayland::DataSourceEvent::DndFinished | wayland::DataSourceEvent::Cancelled, wayland::DataSourceEvent::DndFinished | wayland::DataSourceEvent::Cancelled,
))) => { ))) => {
@ -1393,6 +1406,7 @@ where
return event::Status::Captured; return event::Status::Captured;
} }
} }
#[cfg(feature = "wayland")]
Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DataSource( Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DataSource(
wayland::DataSourceEvent::Cancelled wayland::DataSourceEvent::Cancelled
| wayland::DataSourceEvent::DndFinished | wayland::DataSourceEvent::DndFinished
@ -1404,6 +1418,7 @@ where
return event::Status::Captured; return event::Status::Captured;
} }
} }
#[cfg(feature = "wayland")]
Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DataSource( Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DataSource(
wayland::DataSourceEvent::DndActionAccepted(action), wayland::DataSourceEvent::DndActionAccepted(action),
))) => { ))) => {
@ -1413,7 +1428,7 @@ where
return event::Status::Captured; return event::Status::Captured;
} }
} }
// TODO: handle dnd offer events #[cfg(feature = "wayland")]
Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DndOffer( Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DndOffer(
wayland::DndOfferEvent::Enter { x, y, mime_types }, wayland::DndOfferEvent::Enter { x, y, mime_types },
))) => { ))) => {
@ -1480,6 +1495,7 @@ where
return event::Status::Captured; return event::Status::Captured;
} }
} }
#[cfg(feature = "wayland")]
Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DndOffer( Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DndOffer(
wayland::DndOfferEvent::Motion { x, y }, wayland::DndOfferEvent::Motion { x, y },
))) => { ))) => {
@ -1558,6 +1574,7 @@ where
state.cursor.move_to(position.unwrap_or(0)); state.cursor.move_to(position.unwrap_or(0));
return event::Status::Captured; return event::Status::Captured;
} }
#[cfg(feature = "wayland")]
Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DndOffer( Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DndOffer(
wayland::DndOfferEvent::DropPerformed, wayland::DndOfferEvent::DropPerformed,
))) => { ))) => {
@ -1586,6 +1603,7 @@ where
} }
return event::Status::Ignored; return event::Status::Ignored;
} }
#[cfg(feature = "wayland")]
Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DndOffer( Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DndOffer(
wayland::DndOfferEvent::Leave, wayland::DndOfferEvent::Leave,
))) => { ))) => {
@ -1600,6 +1618,7 @@ where
}; };
return event::Status::Captured; return event::Status::Captured;
} }
#[cfg(feature = "wayland")]
Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DndOffer( Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DndOffer(
wayland::DndOfferEvent::DndData { mime_type, data }, wayland::DndOfferEvent::DndData { mime_type, data },
))) => { ))) => {
@ -1636,6 +1655,7 @@ where
} }
return event::Status::Ignored; return event::Status::Ignored;
} }
#[cfg(feature = "wayland")]
Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DndOffer( Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::DndOffer(
wayland::DndOfferEvent::SourceActions(actions), wayland::DndOfferEvent::SourceActions(actions),
))) => { ))) => {
@ -2016,6 +2036,7 @@ pub fn mouse_interaction(
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TextInputString(String); pub struct TextInputString(String);
#[cfg(feature = "wayland")]
impl DataFromMimeType for TextInputString { impl DataFromMimeType for TextInputString {
fn from_mime_type(&self, mime_type: &str) -> Option<Vec<u8>> { fn from_mime_type(&self, mime_type: &str) -> Option<Vec<u8>> {
if SUPPORTED_MIME_TYPES.contains(&mime_type) { if SUPPORTED_MIME_TYPES.contains(&mime_type) {
@ -2029,9 +2050,11 @@ impl DataFromMimeType for TextInputString {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum DraggingState { pub(crate) enum DraggingState {
Selection, Selection,
#[cfg(feature = "wayland")]
Dnd(DndAction, String), Dnd(DndAction, String),
} }
#[cfg(feature = "wayland")]
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub(crate) enum DndOfferState { pub(crate) enum DndOfferState {
#[default] #[default]
@ -2040,6 +2063,9 @@ pub(crate) enum DndOfferState {
HandlingOffer(Vec<String>, DndAction), HandlingOffer(Vec<String>, DndAction),
Dropped, Dropped,
} }
#[derive(Debug, Default, Clone)]
#[cfg(not(feature = "wayland"))]
pub(crate) struct DndOfferState;
/// The state of a [`TextInput`]. /// The state of a [`TextInput`].
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
@ -2081,6 +2107,7 @@ impl State {
} }
} }
#[cfg(feature = "wayland")]
/// Returns the current value of the dragged text in the [`TextInput`]. /// Returns the current value of the dragged text in the [`TextInput`].
#[must_use] #[must_use]
pub fn dragged_text(&self) -> Option<String> { pub fn dragged_text(&self) -> Option<String> {
@ -2095,7 +2122,7 @@ impl State {
Self { Self {
is_focused: None, is_focused: None,
dragging_state: None, dragging_state: None,
dnd_offer: DndOfferState::None, dnd_offer: DndOfferState::default(),
is_pasting: None, is_pasting: None,
last_click: None, last_click: None,
cursor: Cursor::default(), cursor: Cursor::default(),

View file

@ -1,9 +1,10 @@
//! A text input widget from iced_widgets plus some added details. //! A text input widget from iced widgets plus some added details.
pub mod cursor; pub mod cursor;
pub mod editor; pub mod editor;
pub mod style; mod input;
mod text_input; mod style;
pub mod value; pub mod value;
pub use text_input::*; pub use input::*;
pub use style::{Appearance as TextInputAppearance, StyleSheet as TextInputStyleSheet};