diff --git a/examples/basic_sprite.rs b/examples/basic_sprite.rs index bfdbefb28115ce1a89a059afc05b7ba2c01c572a..09073d0dd2ed969a571185079afa9414ad7c1b80 100644 --- a/examples/basic_sprite.rs +++ b/examples/basic_sprite.rs @@ -27,7 +27,6 @@ fn setup( "馃榾馃榾馃榾 x => y", attrs, ), - text_position: CosmicTextPosition::Center, sprite_bundle: SpriteBundle { sprite: Sprite { custom_size: Some(Vec2::new(primary_window.width(), primary_window.height())), diff --git a/examples/basic_ui.rs b/examples/basic_ui.rs index 2584d4c57c19414a58f3c2e64d08bf754d66acbe..94eb841d7697f97b9589aa2f45bd7359f863794f 100644 --- a/examples/basic_ui.rs +++ b/examples/basic_ui.rs @@ -23,7 +23,6 @@ fn setup(mut commands: Commands, mut font_system: ResMut<CosmicFontSystem>) { vec![("Banana", attrs)], attrs, ), - text_position: CosmicTextPosition::Center, ..default() },)) .id(); diff --git a/examples/font_per_widget.rs b/examples/font_per_widget.rs index f6f6959d825d7800134a9b529e9f8b8ba8ce5bc9..17282745303a8cd5cc5de4f2249dae72f73ab705 100644 --- a/examples/font_per_widget.rs +++ b/examples/font_per_widget.rs @@ -101,7 +101,6 @@ fn setup(mut commands: Commands, mut font_system: ResMut<CosmicFontSystem>) { lines, attrs, ), - text_position: bevy_cosmic_edit::CosmicTextPosition::Center, ..default() }) .id(); @@ -117,7 +116,6 @@ fn setup(mut commands: Commands, mut font_system: ResMut<CosmicFontSystem>) { "Widget 2.\nClick on me =>", attrs_2, ), - text_position: CosmicTextPosition::Center, ..default() }) .id(); diff --git a/examples/multiple_sprites.rs b/examples/multiple_sprites.rs index 13d15f419143813b983dc5ec700b9e95ca2dc179..e8623fcaf16552907c23699ac2a987aa010fd2c3 100644 --- a/examples/multiple_sprites.rs +++ b/examples/multiple_sprites.rs @@ -22,7 +22,6 @@ fn setup( attrs = attrs.color(bevy_color_to_cosmic(Color::PURPLE)); commands.spawn(CosmicEditBundle { - text_position: CosmicTextPosition::Center, fill_color: FillColor(Color::ALICE_BLUE), buffer: CosmicBuffer::new(&mut font_system, Metrics::new(14., 18.)).with_text( &mut font_system, @@ -44,7 +43,6 @@ fn setup( }); commands.spawn(CosmicEditBundle { - text_position: CosmicTextPosition::Center, fill_color: FillColor(Color::GRAY.with_a(0.5)), buffer: CosmicBuffer::new(&mut font_system, Metrics::new(14., 18.)).with_text( &mut font_system, diff --git a/examples/placeholder.rs b/examples/placeholder.rs index bc081eac7c505b91ecf1b0d5160ab98d14ef3124..82d459515548428c7a89a781ad60f275a872f56b 100644 --- a/examples/placeholder.rs +++ b/examples/placeholder.rs @@ -24,7 +24,6 @@ fn setup(mut commands: Commands, mut font_system: ResMut<CosmicFontSystem>) { CosmicEditBundle { buffer: CosmicBuffer::new(&mut font_system, Metrics::new(20., 20.)) .with_rich_text(&mut font_system, vec![("", attrs)], attrs), - text_position: CosmicTextPosition::Center, ..default() }, Placeholder::new( diff --git a/examples/readonly.rs b/examples/readonly.rs index b41ad9f393a78a68bc971b1911bca9f1c7a1eb52..0a76bd89f69ca32326a3256605b251a42ac6d92f 100644 --- a/examples/readonly.rs +++ b/examples/readonly.rs @@ -23,7 +23,6 @@ fn setup(mut commands: Commands, mut font_system: ResMut<CosmicFontSystem>) { // spawn editor let cosmic_edit = commands .spawn(CosmicEditBundle { - text_position: CosmicTextPosition::Center, buffer: CosmicBuffer::new(&mut font_system, Metrics::new(14., 18.)).with_text( &mut font_system, "馃榾馃榾馃榾 x => y\nRead only widget", diff --git a/examples/sprite_and_ui_clickable.rs b/examples/sprite_and_ui_clickable.rs index 31d4e76ce8f5f033d2ca9879082fc40ab209681a..0afb584c4e46d19013cf82c0fe977aeea85d1ed4 100644 --- a/examples/sprite_and_ui_clickable.rs +++ b/examples/sprite_and_ui_clickable.rs @@ -15,6 +15,8 @@ fn setup(mut commands: Commands) { Attrs::new().color(bevy_color_to_cosmic(Color::GREEN)), )), max_lines: CosmicMaxLines(1), + mode: CosmicMode::InfiniteLine, + text_position: CosmicTextPosition::Left { padding: 5 }, ..default() }) .id(); @@ -35,6 +37,8 @@ fn setup(mut commands: Commands) { // Sprite editor commands.spawn((CosmicEditBundle { + max_lines: CosmicMaxLines(1), + mode: CosmicMode::InfiniteLine, sprite_bundle: SpriteBundle { // Sets size of text box sprite: Sprite { diff --git a/src/input.rs b/src/input.rs index 087cef09eeb029f61878ad86930eb2c2a5511f41..9bb5477d732c52e0822f1c48d120e36e0a5019b1 100644 --- a/src/input.rs +++ b/src/input.rs @@ -123,7 +123,7 @@ pub(crate) fn input_mouse( } let (padding_x, padding_y) = match text_position { - CosmicTextPosition::Center => ( + CosmicTextPosition::Center { padding: _ } => ( get_x_offset_center(width * scale_factor, &buffer), get_y_offset_center(height * scale_factor, &buffer), ), @@ -153,7 +153,7 @@ pub(crate) fn input_mouse( camera_transform, ) { let (mut x, y) = point(node_cursor_pos); - x += x_offset.0.unwrap_or((0., 0.)).0 as i32; + x += x_offset.left as i32; if shift { editor.action(&mut font_system.0, Action::Drag { x, y }); } else { @@ -193,7 +193,7 @@ pub(crate) fn input_mouse( camera_transform, ) { let (mut x, y) = point(node_cursor_pos); - x += x_offset.0.unwrap_or((0., 0.)).0 as i32; + x += x_offset.left as i32; if active_editor.is_changed() && !shift { editor.action(&mut font_system.0, Action::Click { x, y }); } else { @@ -355,13 +355,28 @@ pub(crate) fn input_kb( if keys.just_pressed(KeyCode::Backspace) & !readonly { // fix for issue #8 let select = editor.selection(); - if select != Selection::None { - // TODO: fix zero-width selections if needed - // - // if editor.cursor().line == select.line && editor.cursor().index == select.index { - // editor.set_selection(Selection::None); - // } + match select { + Selection::Line(cursor) => { + if editor.cursor().line == cursor.line && editor.cursor().index == cursor.index + { + editor.set_selection(Selection::None); + } + } + Selection::Normal(cursor) => { + if editor.cursor().line == cursor.line && editor.cursor().index == cursor.index + { + editor.set_selection(Selection::None); + } + } + Selection::Word(cursor) => { + if editor.cursor().line == cursor.line && editor.cursor().index == cursor.index + { + editor.set_selection(Selection::None); + } + } + Selection::None => {} } + *is_deleting = true; #[cfg(target_arch = "wasm32")] editor.action(&mut font_system.0, Action::Backspace); @@ -372,6 +387,7 @@ pub(crate) fn input_kb( } if keys.just_pressed(KeyCode::Delete) && !readonly { editor.action(&mut font_system.0, Action::Delete); + editor.with_buffer_mut(|b| b.set_redraw(true)); } if keys.just_pressed(KeyCode::Escape) { editor.action(&mut font_system.0, Action::Escape); diff --git a/src/layout.rs b/src/layout.rs index 2665d9ae1b0dcc12c28c6a6beb83b1bdd35960eb..f5d0bce834a9809cf2fbd15f35922136e5655fd3 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -4,10 +4,10 @@ use cosmic_text::Affinity; use self::buffer::{get_x_offset_center, get_y_offset_center}; -#[derive(Component, Default)] +#[derive(Component, Default, Deref, DerefMut, Debug)] pub struct CosmicPadding(pub Vec2); -#[derive(Component, Default)] +#[derive(Component, Default, Deref, DerefMut)] pub struct CosmicWidgetSize(pub Vec2); pub fn reshape(mut query: Query<&mut CosmicEditor>, mut font_system: ResMut<CosmicFontSystem>) { @@ -46,7 +46,7 @@ pub fn set_padding( } padding.0 = match position { - CosmicTextPosition::Center => Vec2::new( + CosmicTextPosition::Center { padding: _ } => Vec2::new( get_x_offset_center(size.0.x, &buffer) as f32, get_y_offset_center(size.0.y, &buffer) as f32, ), @@ -91,7 +91,7 @@ pub fn set_buffer_size( ) { for (mut buffer, mode, size, position) in query.iter_mut() { let padding_x = match position { - CosmicTextPosition::Center => 0., + CosmicTextPosition::Center { padding: _ } => 0., CosmicTextPosition::TopLeft { padding } => *padding as f32, CosmicTextPosition::Left { padding } => *padding as f32, }; @@ -114,47 +114,58 @@ pub fn new_image_from_default( } } -pub fn set_cursor( +pub fn set_x_offset( mut query: Query<( &mut XOffset, &CosmicMode, &CosmicEditor, - &CosmicBuffer, &CosmicWidgetSize, - &CosmicPadding, + &CosmicTextPosition, )>, ) { - for (mut x_offset, mode, editor, buffer, size, padding) in query.iter_mut() { + for (mut x_offset, mode, editor, size, position) in query.iter_mut() { + if mode != &CosmicMode::InfiniteLine { + return; + } + let mut cursor_x = 0.; - if mode == &CosmicMode::InfiniteLine { - if let Some(line) = buffer.layout_runs().next() { - for (idx, glyph) in line.glyphs.iter().enumerate() { - if editor.cursor().affinity == Affinity::Before { - if idx <= editor.cursor().index { - cursor_x += glyph.w; - } - } else if idx < editor.cursor().index { + let cursor = editor.cursor(); + + if let Some(line) = editor.with_buffer(|b| b.clone()).layout_runs().next() { + for (idx, glyph) in line.glyphs.iter().enumerate() { + if cursor.affinity == Affinity::Before { + if idx <= cursor.index { cursor_x += glyph.w; } else { break; } + } else if idx < cursor.index { + cursor_x += glyph.w; + } else { + break; } } } - if mode == &CosmicMode::InfiniteLine && x_offset.0.is_none() { - *x_offset = XOffset(Some((0., size.0.x - 2. * padding.0.x))); + let padding_x = match position { + CosmicTextPosition::Center { padding } => *padding as f32, + CosmicTextPosition::TopLeft { padding } => *padding as f32, + CosmicTextPosition::Left { padding } => *padding as f32, + }; + + if x_offset.width == 0. { + x_offset.width = size.x - padding_x * 2.; } - if let Some((x_min, x_max)) = x_offset.0 { - if cursor_x > x_max { - let diff = cursor_x - x_max; - *x_offset = XOffset(Some((x_min + diff, cursor_x))); - } - if cursor_x < x_min { - let diff = x_min - cursor_x; - *x_offset = XOffset(Some((cursor_x, x_max - diff))); - } + let right = x_offset.width + x_offset.left; + + if cursor_x > right { + let diff = cursor_x - right; + x_offset.left += diff; + } + if cursor_x < x_offset.left { + let diff = x_offset.left - cursor_x; + x_offset.left -= diff; } } } diff --git a/src/lib.rs b/src/lib.rs index be981847c2a7b0dcef04a622b9901e489eff6597..726f5a242712ffeace3c930240f7ae0691c553c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,8 +30,8 @@ use input::{input_kb, input_mouse, ClickTimer}; #[cfg(target_arch = "wasm32")] use input::{poll_wasm_paste, WasmPaste, WasmPasteAsyncChannel}; use layout::{ - new_image_from_default, reshape, set_buffer_size, set_cursor, set_padding, - set_sprite_size_from_ui, set_widget_size, CosmicPadding, CosmicWidgetSize, + new_image_from_default, reshape, set_buffer_size, set_padding, set_sprite_size_from_ui, + set_widget_size, set_x_offset, CosmicPadding, CosmicWidgetSize, }; use render::{blink_cursor, render_texture, SwashCacheState}; @@ -55,16 +55,17 @@ pub enum CursorConfig { } /// Enum representing the position of the cosmic text. -#[derive(Clone, Component, Default)] +#[derive(Clone, Component)] pub enum CosmicTextPosition { - #[default] - Center, - TopLeft { - padding: i32, - }, - Left { - padding: i32, - }, + Center { padding: i32 }, + TopLeft { padding: i32 }, + Left { padding: i32 }, +} + +impl Default for CosmicTextPosition { + fn default() -> Self { + CosmicTextPosition::Center { padding: 5 } + } } #[derive(Event, Debug)] @@ -77,7 +78,10 @@ pub struct CosmicFontSystem(pub FontSystem); pub struct ReadOnly; // tag component #[derive(Component, Debug, Default)] -pub struct XOffset(Option<(f32, f32)>); +pub struct XOffset { + pub left: f32, + pub width: f32, +} #[derive(Component, Deref, DerefMut)] pub struct CosmicEditor { @@ -170,7 +174,7 @@ impl Default for CosmicEditBundle { visibility: Visibility::Hidden, ..default() }, - x_offset: XOffset(None), + x_offset: Default::default(), padding: Default::default(), widget_size: Default::default(), } @@ -215,7 +219,7 @@ impl Plugin for CosmicEditPlugin { set_widget_size, set_buffer_size, set_padding, - set_cursor, + set_x_offset, ) .chain(); diff --git a/src/render.rs b/src/render.rs index 730d8c08d4680f641f6ea2d58eedd6dd8c4c53a0..78e376ed4d9c069ce0bbc773944d780dd49b0bfc 100644 --- a/src/render.rs +++ b/src/render.rs @@ -4,8 +4,8 @@ use image::{imageops::FilterType, GenericImageView}; use crate::{ layout::{CosmicPadding, CosmicWidgetSize}, - CosmicBackground, CosmicBuffer, CosmicEditor, CosmicFontSystem, CursorColor, DefaultAttrs, - FillColor, ReadOnly, SelectionColor, XOffset, + CosmicBackground, CosmicBuffer, CosmicEditor, CosmicFontSystem, CosmicTextPosition, + CursorColor, DefaultAttrs, FillColor, ReadOnly, SelectionColor, XOffset, }; #[derive(Resource)] @@ -77,6 +77,7 @@ pub(crate) fn render_texture( &CosmicPadding, &XOffset, Option<&ReadOnly>, + &CosmicTextPosition, )>, mut font_system: ResMut<CosmicFontSystem>, mut images: ResMut<Assets<Image>>, @@ -95,6 +96,7 @@ pub(crate) fn render_texture( padding, x_offset, readonly_opt, + position, ) in query.iter_mut() { // Draw background @@ -133,6 +135,12 @@ pub(crate) fn render_texture( .color_opt .unwrap_or(cosmic_text::Color::rgb(0, 0, 0)); + let min_pad = match position { + CosmicTextPosition::Center { padding } => *padding as f32, + CosmicTextPosition::TopLeft { padding } => *padding as f32, + CosmicTextPosition::Left { padding } => *padding as f32, + }; + let draw_closure = |x, y, w, h, color| { for row in 0..h as i32 { for col in 0..w as i32 { @@ -140,8 +148,8 @@ pub(crate) fn render_texture( &mut pixels, size.0.x as i32, size.0.y as i32, - x + col + padding.0.x as i32 - x_offset.0.unwrap_or((0., 0.)).0 as i32, - y + row + padding.0.y as i32, + x + col + padding.x.max(min_pad) as i32 - x_offset.left as i32, + y + row + padding.y as i32, color, ); }