diff options
-rw-r--r-- | Cargo.lock | 103 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | core/src/renderer/null.rs | 2 | ||||
-rw-r--r-- | core/src/text/editor.rs | 41 | ||||
-rw-r--r-- | examples/editor/src/main.rs | 10 | ||||
-rw-r--r-- | examples/todos/Cargo.toml | 2 | ||||
-rw-r--r-- | examples/todos/src/main.rs | 2 | ||||
-rw-r--r-- | graphics/src/text/editor.rs | 17 | ||||
-rw-r--r-- | widget/src/text_editor.rs | 83 | ||||
-rw-r--r-- | winit/Cargo.toml | 3 |
10 files changed, 159 insertions, 105 deletions
@@ -85,7 +85,7 @@ dependencies = [ "ndk-context", "ndk-sys 0.6.0+11769913", "num_enum", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -747,7 +747,7 @@ dependencies = [ "polling 3.7.4", "rustix 0.38.43", "slab", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -821,7 +821,7 @@ dependencies = [ "log", "reqwest", "serde", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing-subscriber", "webbrowser", @@ -935,7 +935,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4274ea815e013e0f9f04a2633423e14194e408a0576c943ce3d14ca56c50031c" dependencies = [ - "thiserror", + "thiserror 1.0.69", "x11rb", ] @@ -1301,24 +1301,24 @@ dependencies = [ ] [[package]] -name = "directories-next" -version = "2.0.0" +name = "directories" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ - "cfg-if", - "dirs-sys-next", + "dirs-sys", ] [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "dirs-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", + "option-ext", "redox_users", - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -2103,7 +2103,7 @@ checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" dependencies = [ "log", "presser", - "thiserror", + "thiserror 1.0.69", "windows 0.58.0", ] @@ -2489,7 +2489,7 @@ dependencies = [ "iced_widget", "iced_winit", "image", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2507,7 +2507,7 @@ dependencies = [ "palette", "rustc-hash 2.1.0", "smol_str", - "thiserror", + "thiserror 1.0.69", "web-time", ] @@ -2542,7 +2542,7 @@ dependencies = [ "lyon_path", "raw-window-handle 0.6.2", "rustc-hash 2.1.0", - "thiserror", + "thiserror 1.0.69", "unicode-segmentation", ] @@ -2562,7 +2562,7 @@ dependencies = [ "iced_tiny_skia", "iced_wgpu", "log", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2573,7 +2573,7 @@ dependencies = [ "iced_core", "iced_futures", "raw-window-handle 0.6.2", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2584,7 +2584,7 @@ dependencies = [ "iced_runtime", "png", "sha2", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2617,7 +2617,7 @@ dependencies = [ "lyon", "resvg", "rustc-hash 2.1.0", - "thiserror", + "thiserror 1.0.69", "wgpu", ] @@ -2633,7 +2633,7 @@ dependencies = [ "pulldown-cmark", "qrcode", "rustc-hash 2.1.0", - "thiserror", + "thiserror 1.0.69", "unicode-segmentation", "url", ] @@ -2648,11 +2648,10 @@ dependencies = [ "log", "rustc-hash 2.1.0", "sysinfo", - "thiserror", + "thiserror 1.0.69", "tracing", "wasm-bindgen-futures", "web-sys", - "winapi", "window_clipboard", "winit", ] @@ -2967,7 +2966,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] @@ -3452,7 +3451,7 @@ dependencies = [ "rustc-hash 1.1.0", "spirv", "termcolor", - "thiserror", + "thiserror 1.0.69", "unicode-xid", ] @@ -3485,7 +3484,7 @@ dependencies = [ "ndk-sys 0.6.0+11769913", "num_enum", "raw-window-handle 0.6.2", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -3985,6 +3984,12 @@ dependencies = [ ] [[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] name = "orbclient" version = "0.3.48" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4582,7 +4587,7 @@ dependencies = [ "rand_chacha", "simd_helpers", "system-deps", - "thiserror", + "thiserror 1.0.69", "v_frame", "wasm-bindgen", ] @@ -4673,13 +4678,13 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.6" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 2.0.11", ] [[package]] @@ -5251,7 +5256,7 @@ dependencies = [ "log", "memmap2", "rustix 0.38.43", - "thiserror", + "thiserror 1.0.69", "wayland-backend", "wayland-client", "wayland-csd-frame", @@ -5498,7 +5503,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror", + "thiserror 1.0.69", "walkdir", "yaml-rust", ] @@ -5613,7 +5618,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] @@ -5628,6 +5642,17 @@ dependencies = [ ] [[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "thread_local" version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5765,7 +5790,7 @@ name = "todos" version = "0.1.0" dependencies = [ "async-std", - "directories-next", + "directories", "iced", "iced_test", "serde", @@ -6024,7 +6049,7 @@ dependencies = [ "rustls 0.22.4", "rustls-pki-types", "sha1", - "thiserror", + "thiserror 1.0.69", "url", "utf-8", ] @@ -6640,7 +6665,7 @@ dependencies = [ "raw-window-handle 0.6.2", "rustc-hash 1.1.0", "smallvec", - "thiserror", + "thiserror 1.0.69", "wgpu-hal", "wgpu-types", ] @@ -6682,7 +6707,7 @@ dependencies = [ "renderdoc-sys", "rustc-hash 1.1.0", "smallvec", - "thiserror", + "thiserror 1.0.69", "wasm-bindgen", "web-sys", "wgpu-types", @@ -6743,7 +6768,7 @@ dependencies = [ "clipboard_wayland", "clipboard_x11", "raw-window-handle 0.6.2", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -188,7 +188,6 @@ wasm-timer = "0.2" web-sys = "0.3.69" web-time = "1.1" wgpu = "23.0" -winapi = "0.3" window_clipboard = "0.4.1" winit = { git = "https://github.com/iced-rs/winit.git", rev = "11414b6aa45699f038114e61b4ddf5102b2d3b4b" } diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index bbcdd8ff..5732c41b 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -137,7 +137,7 @@ impl text::Editor for () { None } - fn line(&self, _index: usize) -> Option<&str> { + fn line(&self, _index: usize) -> Option<text::editor::Line<'_>> { None } diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index cd30db3a..6921c61c 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -3,6 +3,7 @@ use crate::text::highlighter::{self, Highlighter}; use crate::text::{LineHeight, Wrapping}; use crate::{Pixels, Point, Rectangle, Size}; +use std::borrow::Cow; use std::sync::Arc; /// A component that can be used by widgets to edit multi-line text. @@ -28,7 +29,7 @@ pub trait Editor: Sized + Default { fn selection(&self) -> Option<String>; /// Returns the text of the given line in the [`Editor`], if it exists. - fn line(&self, index: usize) -> Option<&str>; + fn line(&self, index: usize) -> Option<Line<'_>>; /// Returns the amount of lines in the [`Editor`]. fn line_count(&self) -> usize; @@ -189,3 +190,41 @@ pub enum Cursor { /// Cursor selecting a range of text Selection(Vec<Rectangle>), } + +/// A line of an [`Editor`]. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct Line<'a> { + /// The raw text of the [`Line`]. + pub text: Cow<'a, str>, + /// The line ending of the [`Line`]. + pub ending: LineEnding, +} + +/// The line ending of a [`Line`]. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub enum LineEnding { + /// Use `\n` for line ending (POSIX-style) + #[default] + Lf, + /// Use `\r\n` for line ending (Windows-style) + CrLf, + /// Use `\r` for line ending (many legacy systems) + Cr, + /// Use `\n\r` for line ending (some legacy systems) + LfCr, + /// No line ending + None, +} + +impl LineEnding { + /// Gets the string representation of the [`LineEnding`]. + pub fn as_str(self) -> &'static str { + match self { + Self::Lf => "\n", + Self::CrLf => "\r\n", + Self::Cr => "\r", + Self::LfCr => "\n\r", + Self::None => "", + } + } +} diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index 7032324a..c039672e 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -117,8 +117,16 @@ impl Editor { } else { self.is_loading = true; + let mut text = self.content.text(); + + if let Some(ending) = self.content.line_ending() { + if !text.ends_with(ending.as_str()) { + text.push_str(ending.as_str()); + } + } + Task::perform( - save_file(self.file.clone(), self.content.text()), + save_file(self.file.clone(), text), Message::FileSaved, ) } diff --git a/examples/todos/Cargo.toml b/examples/todos/Cargo.toml index 16f4fdd2..5d42a88d 100644 --- a/examples/todos/Cargo.toml +++ b/examples/todos/Cargo.toml @@ -15,7 +15,7 @@ uuid = { version = "1.0", features = ["v4", "fast-rng", "serde"] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] async-std.workspace = true -directories-next = "2.0" +directories = "6.0" tracing-subscriber = "0.3" [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 7759552c..7faf742e 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -490,7 +490,7 @@ enum SaveError { impl SavedState { fn path() -> std::path::PathBuf { let mut path = if let Some(project_dirs) = - directories_next::ProjectDirs::from("rs", "Iced", "Todos") + directories::ProjectDirs::from("rs", "Iced", "Todos") { project_dirs.data_dir().into() } else { diff --git a/graphics/src/text/editor.rs b/graphics/src/text/editor.rs index 1f1d0050..c73d189c 100644 --- a/graphics/src/text/editor.rs +++ b/graphics/src/text/editor.rs @@ -9,6 +9,7 @@ use crate::text; use cosmic_text::Edit as _; +use std::borrow::Cow; use std::fmt; use std::sync::{self, Arc}; @@ -89,11 +90,17 @@ impl editor::Editor for Editor { || (buffer.lines.len() == 1 && buffer.lines[0].text().is_empty()) } - fn line(&self, index: usize) -> Option<&str> { - self.buffer() - .lines - .get(index) - .map(cosmic_text::BufferLine::text) + fn line(&self, index: usize) -> Option<editor::Line<'_>> { + self.buffer().lines.get(index).map(|line| editor::Line { + text: Cow::Borrowed(line.text()), + ending: match line.ending() { + cosmic_text::LineEnding::Lf => editor::LineEnding::Lf, + cosmic_text::LineEnding::CrLf => editor::LineEnding::CrLf, + cosmic_text::LineEnding::Cr => editor::LineEnding::Cr, + cosmic_text::LineEnding::LfCr => editor::LineEnding::LfCr, + cosmic_text::LineEnding::None => editor::LineEnding::None, + }, + }) } fn line_count(&self) -> usize { diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index ad852ce9..a3470768 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -50,12 +50,13 @@ use crate::core::{ Rectangle, Shell, Size, SmolStr, Theme, Vector, }; +use std::borrow::Cow; use std::cell::RefCell; use std::fmt; use std::ops::DerefMut; use std::sync::Arc; -pub use text::editor::{Action, Edit, Motion}; +pub use text::editor::{Action, Edit, Line, LineEnding, Motion}; /// A multi-line text input. /// @@ -349,69 +350,47 @@ where } /// Returns the text of the line at the given index, if it exists. - pub fn line( - &self, - index: usize, - ) -> Option<impl std::ops::Deref<Target = str> + '_> { - std::cell::Ref::filter_map(self.0.borrow(), |internal| { - internal.editor.line(index) + pub fn line(&self, index: usize) -> Option<Line<'_>> { + let internal = self.0.borrow(); + let line = internal.editor.line(index)?; + + Some(Line { + text: Cow::Owned(line.text.into_owned()), + ending: line.ending, }) - .ok() } /// Returns an iterator of the text of the lines in the [`Content`]. - pub fn lines( - &self, - ) -> impl Iterator<Item = impl std::ops::Deref<Target = str> + '_> { - struct Lines<'a, Renderer: text::Renderer> { - internal: std::cell::Ref<'a, Internal<Renderer>>, - current: usize, - } - - impl<'a, Renderer: text::Renderer> Iterator for Lines<'a, Renderer> { - type Item = std::cell::Ref<'a, str>; - - fn next(&mut self) -> Option<Self::Item> { - let line = std::cell::Ref::filter_map( - std::cell::Ref::clone(&self.internal), - |internal| internal.editor.line(self.current), - ) - .ok()?; - - self.current += 1; - - Some(line) - } - } - - Lines { - internal: self.0.borrow(), - current: 0, - } + pub fn lines(&self) -> impl Iterator<Item = Line<'_>> { + (0..) + .map(|i| self.line(i)) + .take_while(Option::is_some) + .flatten() } /// Returns the text of the [`Content`]. - /// - /// Lines are joined with `'\n'`. pub fn text(&self) -> String { - let mut text = self.lines().enumerate().fold( - String::new(), - |mut contents, (i, line)| { - if i > 0 { - contents.push('\n'); - } + let mut contents = String::new(); + let mut lines = self.lines().peekable(); - contents.push_str(&line); + while let Some(line) = lines.next() { + contents.push_str(&line.text); - contents - }, - ); - - if !text.ends_with('\n') { - text.push('\n'); + if lines.peek().is_some() { + contents.push_str(if line.ending == LineEnding::None { + LineEnding::default().as_str() + } else { + line.ending.as_str() + }); + } } - text + contents + } + + /// Returns the kind of [`LineEnding`] used for separating lines in the [`Content`]. + pub fn line_ending(&self) -> Option<LineEnding> { + Some(self.line(0)?.ending) } /// Returns the selected text of the [`Content`]. diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 10a6369b..f8f8e26e 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -39,9 +39,6 @@ winit.workspace = true sysinfo.workspace = true sysinfo.optional = true -[target.'cfg(target_os = "windows")'.dependencies] -winapi.workspace = true - [target.'cfg(target_arch = "wasm32")'.dependencies] web-sys.workspace = true web-sys.features = ["Document", "Window", "HtmlCanvasElement"] |