added text edit history / undo (ctrl-z)
This commit is contained in:
@@ -95,6 +95,7 @@ impl TextBuilderOutput for TextEditOutput {
|
|||||||
let mut text = TextEdit {
|
let mut text = TextEdit {
|
||||||
view: TextView::new(buf, builder.attrs, builder.hint.get(ui)),
|
view: TextView::new(buf, builder.attrs, builder.hint.get(ui)),
|
||||||
selection: Default::default(),
|
selection: Default::default(),
|
||||||
|
history: Default::default(),
|
||||||
};
|
};
|
||||||
let font_system = &mut ui.data.text.font_system;
|
let font_system = &mut ui.data.text.font_system;
|
||||||
text.buf
|
text.buf
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use winit::{
|
|||||||
pub struct TextEdit {
|
pub struct TextEdit {
|
||||||
pub(super) view: TextView,
|
pub(super) view: TextView,
|
||||||
pub(super) selection: TextSelection,
|
pub(super) selection: TextSelection,
|
||||||
|
pub(super) history: Vec<(String, TextSelection)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextEdit {
|
impl TextEdit {
|
||||||
@@ -180,6 +181,13 @@ impl<'a> TextEditCtx<'a> {
|
|||||||
text
|
text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, text: &str) {
|
||||||
|
self.text
|
||||||
|
.buf
|
||||||
|
.set_text(self.font_system, text, &Attrs::new(), SHAPING, None);
|
||||||
|
self.text.selection.clear();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn motion(&mut self, motion: Motion) {
|
pub fn motion(&mut self, motion: Motion) {
|
||||||
if let TextSelection::Pos(cursor) = self.text.selection
|
if let TextSelection::Pos(cursor) = self.text.selection
|
||||||
&& let Some(cursor) = self.buf_motion(cursor, motion)
|
&& let Some(cursor) = self.buf_motion(cursor, motion)
|
||||||
@@ -372,6 +380,24 @@ impl<'a> TextEditCtx<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_event(&mut self, event: &KeyEvent, modifiers: &Modifiers) -> TextInputResult {
|
pub fn apply_event(&mut self, event: &KeyEvent, modifiers: &Modifiers) -> TextInputResult {
|
||||||
|
let old = (self.text.content(), self.text.selection);
|
||||||
|
let mut undo = false;
|
||||||
|
let res = self.apply_event_inner(event, modifiers, &mut undo);
|
||||||
|
if undo && let Some((old, selection)) = self.text.history.pop() {
|
||||||
|
self.set(&old);
|
||||||
|
self.text.selection = selection;
|
||||||
|
} else if self.text.content() != old.0 {
|
||||||
|
self.text.history.push(old);
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_event_inner(
|
||||||
|
&mut self,
|
||||||
|
event: &KeyEvent,
|
||||||
|
modifiers: &Modifiers,
|
||||||
|
undo: &mut bool,
|
||||||
|
) -> TextInputResult {
|
||||||
match &event.logical_key {
|
match &event.logical_key {
|
||||||
Key::Named(named) => match named {
|
Key::Named(named) => match named {
|
||||||
NamedKey::Backspace => self.backspace(modifiers.control),
|
NamedKey::Backspace => self.backspace(modifiers.control),
|
||||||
@@ -429,6 +455,9 @@ impl<'a> TextEditCtx<'a> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"z" => {
|
||||||
|
*undo = true;
|
||||||
|
}
|
||||||
_ => self.insert(text),
|
_ => self.insert(text),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -463,7 +492,7 @@ pub enum TextInputResult {
|
|||||||
Paste,
|
Paste,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
pub enum TextSelection {
|
pub enum TextSelection {
|
||||||
#[default]
|
#[default]
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
layout::{FnTag, Ui, Widget, WidgetLike, WidgetTag},
|
layout::{Ui, WidgetLike},
|
||||||
util::{Id, RefCounter},
|
util::{Id, RefCounter},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -141,49 +141,6 @@ pub struct IdFnTag;
|
|||||||
pub trait WidgetIdFn<W>: FnOnce(&mut Ui) -> WidgetId<W> {}
|
pub trait WidgetIdFn<W>: FnOnce(&mut Ui) -> WidgetId<W> {}
|
||||||
impl<W, F: FnOnce(&mut Ui) -> WidgetId<W>> WidgetIdFn<W> for F {}
|
impl<W, F: FnOnce(&mut Ui) -> WidgetId<W>> WidgetIdFn<W> for F {}
|
||||||
|
|
||||||
/// TODO: does this ever make sense to use? it allows for invalid ids
|
|
||||||
pub trait Idable<Tag> {
|
|
||||||
type Widget: Widget;
|
|
||||||
fn set(self, ui: &mut Ui, id: &WidgetId<Self::Widget>);
|
|
||||||
fn id(self, id: &WidgetId<Self::Widget>) -> impl WidgetIdFn<Self::Widget>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
let id = id.clone();
|
|
||||||
move |ui| {
|
|
||||||
self.set(ui, &id);
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn id_static(self, id: StaticWidgetId<Self::Widget>) -> impl WidgetIdFn<Self::Widget>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
move |ui| {
|
|
||||||
let id = id.id(&ui.send);
|
|
||||||
self.set(ui, &id);
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: Widget> Idable<WidgetTag> for W {
|
|
||||||
type Widget = W;
|
|
||||||
|
|
||||||
fn set(self, ui: &mut Ui, id: &WidgetId<Self::Widget>) {
|
|
||||||
ui.set(id, self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: FnOnce(&mut Ui) -> W, W: Widget> Idable<FnTag> for F {
|
|
||||||
type Widget = W;
|
|
||||||
|
|
||||||
fn set(self, ui: &mut Ui, id: &WidgetId<Self::Widget>) {
|
|
||||||
let w = self(ui);
|
|
||||||
ui.set(id, w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: 'static> WidgetLike<IdTag> for WidgetId<W> {
|
impl<W: 'static> WidgetLike<IdTag> for WidgetId<W> {
|
||||||
type Widget = W;
|
type Widget = W;
|
||||||
fn add(self, _: &mut Ui) -> WidgetId<W> {
|
fn add(self, _: &mut Ui) -> WidgetId<W> {
|
||||||
|
|||||||
@@ -52,15 +52,11 @@ impl Ui {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn push<W: Widget>(&mut self, w: W) -> WidgetId<W> {
|
pub fn push<W: Widget>(&mut self, w: W) -> WidgetId<W> {
|
||||||
let id = self.id();
|
let id = self.new_id();
|
||||||
self.data.widgets.insert(id.id, w);
|
self.data.widgets.insert(id.id, w);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set<W: Widget>(&mut self, id: &WidgetId<W>, w: W) {
|
|
||||||
self.data.widgets.insert(id.id, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_root<Tag>(&mut self, w: impl WidgetLike<Tag>) {
|
pub fn set_root<Tag>(&mut self, w: impl WidgetLike<Tag>) {
|
||||||
self.root = Some(w.add(self).any());
|
self.root = Some(w.add(self).any());
|
||||||
self.full_redraw = true;
|
self.full_redraw = true;
|
||||||
@@ -78,7 +74,7 @@ impl Ui {
|
|||||||
self.data.widgets.get_mut(id)
|
self.data.widgets.get_mut(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id<W: Widget>(&mut self) -> WidgetId<W> {
|
fn new_id<W: Widget>(&mut self) -> WidgetId<W> {
|
||||||
WidgetId::new(
|
WidgetId::new(
|
||||||
self.data.widgets.reserve(),
|
self.data.widgets.reserve(),
|
||||||
TypeId::of::<W>(),
|
TypeId::of::<W>(),
|
||||||
@@ -87,11 +83,6 @@ impl Ui {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id_static<W: Widget>(&mut self) -> StaticWidgetId<W> {
|
|
||||||
let id = self.id();
|
|
||||||
id.into_static()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle {
|
pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle {
|
||||||
self.data.textures.add(image)
|
self.data.textures.add(image)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user