Add selection
This commit is contained in:
parent
165ea1c0da
commit
190b41f387
3 changed files with 154 additions and 68 deletions
|
|
@ -9,7 +9,7 @@ use cosmic::iced_native::{
|
||||||
layout::{self, Layout},
|
layout::{self, Layout},
|
||||||
mouse::{self, Button, Event as MouseEvent, ScrollDelta},
|
mouse::{self, Button, Event as MouseEvent, ScrollDelta},
|
||||||
renderer,
|
renderer,
|
||||||
widget::{self, Widget},
|
widget::{self, tree, Widget},
|
||||||
};
|
};
|
||||||
use cosmic_text::{
|
use cosmic_text::{
|
||||||
TextAction,
|
TextAction,
|
||||||
|
|
@ -77,6 +77,14 @@ where
|
||||||
Renderer: renderer::Renderer,
|
Renderer: renderer::Renderer,
|
||||||
Renderer::Theme: StyleSheet,
|
Renderer::Theme: StyleSheet,
|
||||||
{
|
{
|
||||||
|
fn tag(&self) -> tree::Tag {
|
||||||
|
tree::Tag::of::<State>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state(&self) -> tree::State {
|
||||||
|
tree::State::new(State::new())
|
||||||
|
}
|
||||||
|
|
||||||
fn width(&self) -> Length {
|
fn width(&self) -> Length {
|
||||||
Length::Fill
|
Length::Fill
|
||||||
}
|
}
|
||||||
|
|
@ -194,7 +202,7 @@ where
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
_state: &mut widget::Tree,
|
tree: &mut widget::Tree,
|
||||||
event: Event,
|
event: Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
|
|
@ -202,79 +210,85 @@ where
|
||||||
_clipboard: &mut dyn Clipboard,
|
_clipboard: &mut dyn Clipboard,
|
||||||
_shell: &mut Shell<'_, Message>,
|
_shell: &mut Shell<'_, Message>,
|
||||||
) -> Status {
|
) -> Status {
|
||||||
|
let state = tree.state.downcast_mut::<State>();
|
||||||
let mut buffer = self.buffer.lock().unwrap();
|
let mut buffer = self.buffer.lock().unwrap();
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::Keyboard(key_event) => match key_event {
|
Event::Keyboard(KeyEvent::KeyPressed { key_code, modifiers }) => match key_code {
|
||||||
KeyEvent::KeyPressed { key_code, modifiers } => {
|
KeyCode::Left => {
|
||||||
match key_code {
|
buffer.action(TextAction::Left);
|
||||||
KeyCode::Left => {
|
return Status::Captured;
|
||||||
buffer.action(TextAction::Left);
|
|
||||||
Status::Captured
|
|
||||||
},
|
|
||||||
KeyCode::Right => {
|
|
||||||
buffer.action(TextAction::Right);
|
|
||||||
Status::Captured
|
|
||||||
},
|
|
||||||
KeyCode::Up => {
|
|
||||||
buffer.action(TextAction::Up);
|
|
||||||
Status::Captured
|
|
||||||
},
|
|
||||||
KeyCode::Down => {
|
|
||||||
buffer.action(TextAction::Down);
|
|
||||||
Status::Captured
|
|
||||||
},
|
|
||||||
KeyCode::Backspace => {
|
|
||||||
buffer.action(TextAction::Backspace);
|
|
||||||
Status::Captured
|
|
||||||
},
|
|
||||||
KeyCode::Delete => {
|
|
||||||
buffer.action(TextAction::Delete);
|
|
||||||
Status::Captured
|
|
||||||
},
|
|
||||||
KeyCode::PageUp => {
|
|
||||||
buffer.action(TextAction::PageUp);
|
|
||||||
Status::Captured
|
|
||||||
},
|
|
||||||
KeyCode::PageDown => {
|
|
||||||
buffer.action(TextAction::PageDown);
|
|
||||||
Status::Captured
|
|
||||||
},
|
|
||||||
_ => Status::Ignored,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
KeyEvent::CharacterReceived(character) => {
|
KeyCode::Right => {
|
||||||
buffer.action(TextAction::Insert(character));
|
buffer.action(TextAction::Right);
|
||||||
Status::Captured
|
return Status::Captured;
|
||||||
},
|
},
|
||||||
_ => Status::Ignored,
|
KeyCode::Up => {
|
||||||
|
buffer.action(TextAction::Up);
|
||||||
|
return Status::Captured;
|
||||||
|
},
|
||||||
|
KeyCode::Down => {
|
||||||
|
buffer.action(TextAction::Down);
|
||||||
|
return Status::Captured;
|
||||||
|
},
|
||||||
|
KeyCode::Backspace => {
|
||||||
|
buffer.action(TextAction::Backspace);
|
||||||
|
return Status::Captured;
|
||||||
|
},
|
||||||
|
KeyCode::Delete => {
|
||||||
|
buffer.action(TextAction::Delete);
|
||||||
|
return Status::Captured;
|
||||||
|
},
|
||||||
|
KeyCode::PageUp => {
|
||||||
|
buffer.action(TextAction::PageUp);
|
||||||
|
return Status::Captured;
|
||||||
|
},
|
||||||
|
KeyCode::PageDown => {
|
||||||
|
buffer.action(TextAction::PageDown);
|
||||||
|
return Status::Captured;
|
||||||
|
},
|
||||||
|
_ => ()
|
||||||
},
|
},
|
||||||
Event::Mouse(mouse_event) => match mouse_event {
|
Event::Keyboard(KeyEvent::CharacterReceived(character)) => {
|
||||||
MouseEvent::ButtonPressed(button) => match button {
|
buffer.action(TextAction::Insert(character));
|
||||||
Button::Left => if layout.bounds().contains(cursor_position) {
|
return Status::Captured;
|
||||||
buffer.action(TextAction::Click {
|
},
|
||||||
x: (cursor_position.x - layout.bounds().x) as i32,
|
Event::Mouse(MouseEvent::ButtonPressed(Button::Left)) => {
|
||||||
y: (cursor_position.y - layout.bounds().y) as i32,
|
if layout.bounds().contains(cursor_position) {
|
||||||
});
|
buffer.action(TextAction::Click {
|
||||||
Status::Captured
|
x: (cursor_position.x - layout.bounds().x) as i32,
|
||||||
} else {
|
y: (cursor_position.y - layout.bounds().y) as i32,
|
||||||
Status::Ignored
|
});
|
||||||
},
|
state.is_dragging = true;
|
||||||
_ => Status::Ignored,
|
return Status::Captured;
|
||||||
},
|
|
||||||
MouseEvent::WheelScrolled { delta } => match delta {
|
|
||||||
ScrollDelta::Lines { x, y } => {
|
|
||||||
buffer.action(TextAction::Scroll {
|
|
||||||
lines: (-y * 6.0) as i32,
|
|
||||||
});
|
|
||||||
Status::Captured
|
|
||||||
},
|
|
||||||
_ => Status::Ignored,
|
|
||||||
}
|
}
|
||||||
_ => Status::Ignored,
|
|
||||||
},
|
},
|
||||||
_ => Status::Ignored,
|
Event::Mouse(MouseEvent::ButtonReleased(Button::Left)) => {
|
||||||
|
state.is_dragging = false;
|
||||||
|
return Status::Captured;
|
||||||
|
},
|
||||||
|
Event::Mouse(MouseEvent::CursorMoved { .. }) => {
|
||||||
|
if state.is_dragging {
|
||||||
|
buffer.action(TextAction::Drag {
|
||||||
|
x: (cursor_position.x - layout.bounds().x) as i32,
|
||||||
|
y: (cursor_position.y - layout.bounds().y) as i32,
|
||||||
|
});
|
||||||
|
return Status::Captured;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Event::Mouse(MouseEvent::WheelScrolled { delta }) => match delta {
|
||||||
|
ScrollDelta::Lines { x, y } => {
|
||||||
|
buffer.action(TextAction::Scroll {
|
||||||
|
lines: (-y * 6.0) as i32,
|
||||||
|
});
|
||||||
|
return Status::Captured;
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
_ => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status::Ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,3 +301,15 @@ where
|
||||||
Self::new(text_box)
|
Self::new(text_box)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
pub struct State {
|
||||||
|
is_dragging: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
/// Creates a new [`State`].
|
||||||
|
pub fn new() -> State {
|
||||||
|
State::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ fn main() {
|
||||||
mouse_x = event.x;
|
mouse_x = event.x;
|
||||||
mouse_y = event.y;
|
mouse_y = event.y;
|
||||||
if mouse_left {
|
if mouse_left {
|
||||||
buffer.action(TextAction::Click {
|
buffer.action(TextAction::Drag {
|
||||||
x: mouse_x - line_x,
|
x: mouse_x - line_x,
|
||||||
y: mouse_y
|
y: mouse_y
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ pub enum TextAction {
|
||||||
Insert(char),
|
Insert(char),
|
||||||
/// Mouse click at specified position
|
/// Mouse click at specified position
|
||||||
Click { x: i32, y: i32 },
|
Click { x: i32, y: i32 },
|
||||||
|
/// Mouse drag to specified position
|
||||||
|
Drag { x: i32, y: i32 },
|
||||||
/// Scroll specified number of lines
|
/// Scroll specified number of lines
|
||||||
Scroll { lines: i32 },
|
Scroll { lines: i32 },
|
||||||
}
|
}
|
||||||
|
|
@ -99,6 +101,7 @@ pub struct TextBuffer<'a> {
|
||||||
height: i32,
|
height: i32,
|
||||||
scroll: i32,
|
scroll: i32,
|
||||||
cursor: TextCursor,
|
cursor: TextCursor,
|
||||||
|
select_opt: Option<TextCursor>,
|
||||||
pub redraw: bool,
|
pub redraw: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,6 +125,7 @@ impl<'a> TextBuffer<'a> {
|
||||||
height: 0,
|
height: 0,
|
||||||
scroll: 0,
|
scroll: 0,
|
||||||
cursor: TextCursor::default(),
|
cursor: TextCursor::default(),
|
||||||
|
select_opt: None,
|
||||||
redraw: false,
|
redraw: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -401,6 +405,13 @@ impl<'a> TextBuffer<'a> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TextAction::Click { x, y } => {
|
TextAction::Click { x, y } => {
|
||||||
|
self.select_opt = None;
|
||||||
|
self.click(x, y);
|
||||||
|
},
|
||||||
|
TextAction::Drag { x, y } => {
|
||||||
|
if self.select_opt.is_none() {
|
||||||
|
self.select_opt = Some(self.cursor);
|
||||||
|
}
|
||||||
self.click(x, y);
|
self.click(x, y);
|
||||||
},
|
},
|
||||||
TextAction::Scroll { lines } => {
|
TextAction::Scroll { lines } => {
|
||||||
|
|
@ -472,7 +483,56 @@ impl<'a> TextBuffer<'a> {
|
||||||
.take(cmp::max(0, self.lines()) as usize)
|
.take(cmp::max(0, self.lines()) as usize)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
if self.cursor.line == line_i + self.scroll() as usize {
|
let line_i_scrolled = line_i + cmp::max(0, self.scroll()) as usize;
|
||||||
|
|
||||||
|
// Highlight selection (TODO: HIGHLIGHT COLOR!)
|
||||||
|
if let Some(select) = self.select_opt {
|
||||||
|
let (start, end) = if select.line < self.cursor.line {
|
||||||
|
(select, self.cursor)
|
||||||
|
} else if select.line > self.cursor.line {
|
||||||
|
(self.cursor, select)
|
||||||
|
} else {
|
||||||
|
/* select.line == self.cursor.line */
|
||||||
|
if select.glyph < self.cursor.glyph {
|
||||||
|
(select, self.cursor)
|
||||||
|
} else {
|
||||||
|
/* select.glyph >= self.cursor.glyph */
|
||||||
|
(self.cursor, select)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if line_i_scrolled >= start.line && line_i_scrolled <= end.line {
|
||||||
|
let start_glyph = if start.line == line_i_scrolled {
|
||||||
|
start.glyph
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let end_glyph = if end.line == line_i_scrolled {
|
||||||
|
end.glyph
|
||||||
|
} else {
|
||||||
|
line.glyphs.len()
|
||||||
|
};
|
||||||
|
|
||||||
|
let start_x = line.glyphs.get(start_glyph).map_or(0, |glyph| {
|
||||||
|
glyph.x as i32
|
||||||
|
});
|
||||||
|
let end_x = line.glyphs.get(end_glyph).map_or(self.width, |glyph| {
|
||||||
|
(glyph.x + glyph.w) as i32
|
||||||
|
});
|
||||||
|
|
||||||
|
f(
|
||||||
|
start_x,
|
||||||
|
line_y - font_size,
|
||||||
|
cmp::max(0, end_x - start_x) as u32,
|
||||||
|
line_height as u32,
|
||||||
|
0x33_00_00_00 | (color & 0xFF_FF_FF)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw cursor
|
||||||
|
if self.cursor.line == line_i_scrolled {
|
||||||
if self.cursor.glyph >= line.glyphs.len() {
|
if self.cursor.glyph >= line.glyphs.len() {
|
||||||
let x = match line.glyphs.last() {
|
let x = match line.glyphs.last() {
|
||||||
Some(glyph) => glyph.x + glyph.w,
|
Some(glyph) => glyph.x + glyph.w,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue