event system!!!
This commit is contained in:
2
TODO
2
TODO
@@ -1,6 +1,6 @@
|
|||||||
images
|
images
|
||||||
settings (sampler)
|
settings (sampler)
|
||||||
abstract sensors to work with any event, maybe associate data as well?
|
consider unsafe cell
|
||||||
|
|
||||||
really weird limitation:
|
really weird limitation:
|
||||||
I don't think you can currently remove an element from a parent and put it in a child of the same parent
|
I don't think you can currently remove an element from a parent and put it in a child of the same parent
|
||||||
|
|||||||
0
src/core/event.rs
Normal file
0
src/core/event.rs
Normal file
@@ -1,6 +1,9 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use std::ops::{BitOr, Deref, DerefMut};
|
use std::{
|
||||||
|
ops::{BitOr, Deref, DerefMut},
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
layout::{UiModule, UiRegion, Vec2},
|
layout::{UiModule, UiRegion, Vec2},
|
||||||
@@ -11,12 +14,14 @@ pub trait CursorCtx {
|
|||||||
fn cursor_state(&self) -> &CursorState;
|
fn cursor_state(&self) -> &CursorState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
pub enum Button {
|
pub enum Button {
|
||||||
Left,
|
Left,
|
||||||
Right,
|
Right,
|
||||||
Middle,
|
Middle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
pub enum Sense {
|
pub enum Sense {
|
||||||
PressStart(Button),
|
PressStart(Button),
|
||||||
Pressing(Button),
|
Pressing(Button),
|
||||||
@@ -78,7 +83,7 @@ pub enum ActivationState {
|
|||||||
|
|
||||||
pub struct Sensor<Ctx> {
|
pub struct Sensor<Ctx> {
|
||||||
pub senses: Senses,
|
pub senses: Senses,
|
||||||
pub f: Box<dyn SenseFn<Ctx>>,
|
pub f: Rc<dyn SenseFn<Ctx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type SensorMap<Ctx> = HashMap<Id, SensorGroup<Ctx>>;
|
pub type SensorMap<Ctx> = HashMap<Id, SensorGroup<Ctx>>;
|
||||||
@@ -88,13 +93,14 @@ pub struct SensorGroup<Ctx> {
|
|||||||
pub sensors: Vec<Sensor<Ctx>>,
|
pub sensors: Vec<Sensor<Ctx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct SenseData {
|
pub struct SenseData {
|
||||||
pub cursor: Vec2,
|
pub cursor: Vec2,
|
||||||
pub size: Vec2,
|
pub size: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SenseFn<Ctx>: FnMut(&mut Ctx, SenseData) + 'static {}
|
pub trait SenseFn<Ctx>: Fn(&mut Ctx, SenseData) + 'static {}
|
||||||
impl<F: FnMut(&mut Ctx, SenseData) + 'static, Ctx> SenseFn<Ctx> for F {}
|
impl<F: Fn(&mut Ctx, SenseData) + 'static, Ctx> SenseFn<Ctx> for F {}
|
||||||
|
|
||||||
pub struct SensorModule<Ctx> {
|
pub struct SensorModule<Ctx> {
|
||||||
map: SensorMap<Ctx>,
|
map: SensorMap<Ctx>,
|
||||||
@@ -242,24 +248,48 @@ impl<Ctx: 'static> Event<Ctx> for Senses {
|
|||||||
type Data = SenseData;
|
type Data = SenseData;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: 'static> EventModule<Ctx, Senses> for SensorModule<Ctx> {
|
|
||||||
fn register(&mut self, id: Id, senses: Senses, f: impl EventFn<Ctx, SenseData>) {
|
|
||||||
// TODO: does not add to active if currently active
|
|
||||||
self.map.entry(id).or_default().sensors.push(Sensor {
|
|
||||||
senses,
|
|
||||||
f: Box::new(f),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx: 'static> Event<Ctx> for Sense {
|
impl<Ctx: 'static> Event<Ctx> for Sense {
|
||||||
type Module = SensorModule<Ctx>;
|
type Module = SensorModule<Ctx>;
|
||||||
type Data = SenseData;
|
type Data = SenseData;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: 'static> EventModule<Ctx, Sense> for SensorModule<Ctx> {
|
impl<E: Event<Ctx, Data = <Senses as Event<Ctx>>::Data> + Into<Senses>, Ctx: 'static>
|
||||||
fn register(&mut self, id: Id, sense: Sense, f: impl EventFn<Ctx, SenseData>) {
|
EventModule<E, Ctx> for SensorModule<Ctx>
|
||||||
self.register(id, Senses::from(sense), f);
|
{
|
||||||
|
fn register(&mut self, id: Id, senses: E, f: impl EventFn<Ctx, <E as Event<Ctx>>::Data>) {
|
||||||
|
// TODO: does not add to active if currently active
|
||||||
|
self.map.entry(id).or_default().sensors.push(Sensor {
|
||||||
|
senses: senses.into(),
|
||||||
|
f: Rc::new(f),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run<'a>(
|
||||||
|
&mut self,
|
||||||
|
id: &Id,
|
||||||
|
event: E,
|
||||||
|
) -> Option<impl Fn(&mut Ctx, E::Data) + use<'a, E, Ctx>> {
|
||||||
|
let senses = event.into();
|
||||||
|
if let Some(group) = self.map.get_mut(id) {
|
||||||
|
let fs: Vec<_> = group
|
||||||
|
.sensors
|
||||||
|
.iter()
|
||||||
|
.filter_map(|sensor| {
|
||||||
|
if sensor.senses.iter().any(|s| senses.contains(s)) {
|
||||||
|
Some(sensor.f.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Some(move |ctx: &mut Ctx, data: SenseData| {
|
||||||
|
for f in &fs {
|
||||||
|
f(ctx, data.clone());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use cosmic_text::{Attrs, Cursor, Family, FontSystem, Metrics, Motion, Shaping};
|
use cosmic_text::{Affinity, Attrs, Cursor, Family, FontSystem, Metrics, Motion, Shaping};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
use winit::{
|
use winit::{
|
||||||
event::KeyEvent,
|
event::KeyEvent,
|
||||||
@@ -133,6 +133,11 @@ impl<'a> TextEditCtx<'a> {
|
|||||||
self.text
|
self.text
|
||||||
.buf
|
.buf
|
||||||
.set_text(self.font_system, "", &Attrs::new(), Shaping::Advanced);
|
.set_text(self.font_system, "", &Attrs::new(), Shaping::Advanced);
|
||||||
|
if let Some(cursor) = &mut self.text.cursor {
|
||||||
|
cursor.line = 0;
|
||||||
|
cursor.index = 0;
|
||||||
|
cursor.affinity = Affinity::default();
|
||||||
|
}
|
||||||
text
|
text
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,13 +224,19 @@ impl<'a> TextEditCtx<'a> {
|
|||||||
self.text.cursor = None;
|
self.text.cursor = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_event(&mut self, event: &KeyEvent) -> TextInputResult {
|
pub fn apply_event(&mut self, event: &KeyEvent, modifiers: &Modifiers) -> TextInputResult {
|
||||||
match &event.logical_key {
|
match &event.logical_key {
|
||||||
Key::Named(named) => match named {
|
Key::Named(named) => match named {
|
||||||
NamedKey::Backspace => self.backspace(),
|
NamedKey::Backspace => self.backspace(),
|
||||||
NamedKey::Delete => self.delete(),
|
NamedKey::Delete => self.delete(),
|
||||||
NamedKey::Space => self.insert(" "),
|
NamedKey::Space => self.insert(" "),
|
||||||
NamedKey::Enter => self.insert("\n"),
|
NamedKey::Enter => {
|
||||||
|
if modifiers.shift {
|
||||||
|
self.newline();
|
||||||
|
} else {
|
||||||
|
return TextInputResult::Submit;
|
||||||
|
}
|
||||||
|
}
|
||||||
NamedKey::ArrowRight => self.motion(Motion::Right),
|
NamedKey::ArrowRight => self.motion(Motion::Right),
|
||||||
NamedKey::ArrowLeft => self.motion(Motion::Left),
|
NamedKey::ArrowLeft => self.motion(Motion::Left),
|
||||||
NamedKey::ArrowUp => self.motion(Motion::Up),
|
NamedKey::ArrowUp => self.motion(Motion::Up),
|
||||||
@@ -243,10 +254,24 @@ impl<'a> TextEditCtx<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Modifiers {
|
||||||
|
pub shift: bool,
|
||||||
|
pub control: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Modifiers {
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.shift = false;
|
||||||
|
self.control = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum TextInputResult {
|
pub enum TextInputResult {
|
||||||
Used,
|
Used,
|
||||||
Unused,
|
Unused,
|
||||||
Unfocus,
|
Unfocus,
|
||||||
|
Submit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextInputResult {
|
impl TextInputResult {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
use std::{hash::Hash, rc::Rc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
layout::{IdFnTag, Ui, UiModule, Widget, WidgetId, WidgetIdFn, WidgetLike},
|
layout::{IdFnTag, Ui, UiModule, Widget, WidgetId, WidgetIdFn, WidgetLike},
|
||||||
util::Id,
|
util::{HashMap, Id},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait UiCtx {
|
pub trait UiCtx {
|
||||||
@@ -14,16 +16,12 @@ impl UiCtx for Ui {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Event<Ctx>: Sized {
|
pub trait Event<Ctx>: Sized {
|
||||||
type Module: UiModule + EventModule<Ctx, Self> + Default;
|
type Module: EventModule<Self, Ctx>;
|
||||||
type Data;
|
type Data: Clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait EventModule<Ctx, E: Event<Ctx>> {
|
pub trait EventFn<Ctx, Data>: Fn(&mut Ctx, Data) + 'static {}
|
||||||
fn register(&mut self, id: Id, event: E, f: impl EventFn<Ctx, E::Data>);
|
impl<F: Fn(&mut Ctx, Data) + 'static, Ctx, Data> EventFn<Ctx, Data> for F {}
|
||||||
}
|
|
||||||
|
|
||||||
pub trait EventFn<Ctx, Data>: FnMut(&mut Ctx, Data) + 'static {}
|
|
||||||
impl<F: FnMut(&mut Ctx, Data) + 'static, Ctx, Data> EventFn<Ctx, Data> for F {}
|
|
||||||
|
|
||||||
pub trait Eventable<W, Tag> {
|
pub trait Eventable<W, Tag> {
|
||||||
fn on<E: Event<Ctx>, Ctx>(
|
fn on<E: Event<Ctx>, Ctx>(
|
||||||
@@ -35,7 +33,7 @@ pub trait Eventable<W, Tag> {
|
|||||||
fn id_on<E: Event<Ctx>, Ctx>(
|
fn id_on<E: Event<Ctx>, Ctx>(
|
||||||
self,
|
self,
|
||||||
event: E,
|
event: E,
|
||||||
f: impl FnMut(&WidgetId<W>, &mut Ctx, E::Data) + 'static,
|
f: impl Fn(&WidgetId<W>, &mut Ctx, E::Data) + 'static,
|
||||||
) -> impl WidgetIdFn<W> + Eventable<W, IdFnTag>
|
) -> impl WidgetIdFn<W> + Eventable<W, IdFnTag>
|
||||||
where
|
where
|
||||||
W: Widget;
|
W: Widget;
|
||||||
@@ -43,7 +41,7 @@ pub trait Eventable<W, Tag> {
|
|||||||
fn edit_on<E: Event<Ui>>(
|
fn edit_on<E: Event<Ui>>(
|
||||||
self,
|
self,
|
||||||
event: E,
|
event: E,
|
||||||
f: impl FnMut(&mut W, E::Data) + 'static,
|
f: impl Fn(&mut W, E::Data) + 'static,
|
||||||
) -> impl WidgetIdFn<W> + Eventable<W, IdFnTag>
|
) -> impl WidgetIdFn<W> + Eventable<W, IdFnTag>
|
||||||
where
|
where
|
||||||
W: Widget;
|
W: Widget;
|
||||||
@@ -67,7 +65,7 @@ impl<W: WidgetLike<Tag>, Tag> Eventable<W::Widget, Tag> for W {
|
|||||||
fn id_on<E: Event<Ctx>, Ctx>(
|
fn id_on<E: Event<Ctx>, Ctx>(
|
||||||
self,
|
self,
|
||||||
event: E,
|
event: E,
|
||||||
mut f: impl FnMut(&WidgetId<W::Widget>, &mut Ctx, E::Data) + 'static,
|
mut f: impl Fn(&WidgetId<W::Widget>, &mut Ctx, E::Data) + 'static,
|
||||||
) -> impl WidgetIdFn<W::Widget>
|
) -> impl WidgetIdFn<W::Widget>
|
||||||
where
|
where
|
||||||
W::Widget: Widget,
|
W::Widget: Widget,
|
||||||
@@ -81,7 +79,7 @@ impl<W: WidgetLike<Tag>, Tag> Eventable<W::Widget, Tag> for W {
|
|||||||
fn edit_on<E: Event<Ui>>(
|
fn edit_on<E: Event<Ui>>(
|
||||||
self,
|
self,
|
||||||
event: E,
|
event: E,
|
||||||
mut f: impl FnMut(&mut W::Widget, E::Data) + 'static,
|
mut f: impl Fn(&mut W::Widget, E::Data) + 'static,
|
||||||
) -> impl WidgetIdFn<W::Widget>
|
) -> impl WidgetIdFn<W::Widget>
|
||||||
where
|
where
|
||||||
W::Widget: Widget,
|
W::Widget: Widget,
|
||||||
@@ -89,3 +87,116 @@ impl<W: WidgetLike<Tag>, Tag> Eventable<W::Widget, Tag> for W {
|
|||||||
self.id_on(event, move |id, ui, pos| f(&mut ui[id], pos))
|
self.id_on(event, move |id, ui, pos| f(&mut ui[id], pos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait DefaultEvent: Hash + Eq + 'static {
|
||||||
|
type Data: Clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: DefaultEvent, Ctx: 'static> Event<Ctx> for E {
|
||||||
|
type Module = DefaultEventModule<E, Ctx>;
|
||||||
|
type Data = E::Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait EventModule<E: Event<Ctx>, Ctx>: UiModule + Default {
|
||||||
|
fn register(&mut self, id: Id, event: E, f: impl EventFn<Ctx, E::Data>);
|
||||||
|
fn run<'a>(
|
||||||
|
&mut self,
|
||||||
|
id: &Id,
|
||||||
|
event: E,
|
||||||
|
) -> Option<impl Fn(&mut Ctx, E::Data) + use<'a, Self, E, Ctx>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventFnMap<Ctx, Data> = HashMap<Id, Vec<Rc<dyn EventFn<Ctx, Data>>>>;
|
||||||
|
pub struct DefaultEventModule<E: Event<Ctx>, Ctx> {
|
||||||
|
map: HashMap<E, EventFnMap<Ctx, <E as Event<Ctx>>::Data>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Event<Ctx> + 'static, Ctx: 'static> UiModule for DefaultEventModule<E, Ctx> {
|
||||||
|
fn on_remove(&mut self, id: &Id) {
|
||||||
|
for map in self.map.values_mut() {
|
||||||
|
map.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait HashableEvent<Ctx>: Event<Ctx> + Hash + Eq + 'static {}
|
||||||
|
impl<E: Event<Ctx> + Hash + Eq + 'static, Ctx> HashableEvent<Ctx> for E {}
|
||||||
|
|
||||||
|
impl<E: HashableEvent<Ctx>, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<E, Ctx> {
|
||||||
|
fn register(&mut self, id: Id, event: E, f: impl EventFn<Ctx, <E as Event<Ctx>>::Data>) {
|
||||||
|
self.map
|
||||||
|
.entry(event)
|
||||||
|
.or_default()
|
||||||
|
.entry(id)
|
||||||
|
.or_default()
|
||||||
|
.push(Rc::new(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run<'a>(
|
||||||
|
&mut self,
|
||||||
|
id: &Id,
|
||||||
|
event: E,
|
||||||
|
) -> Option<impl Fn(&mut Ctx, E::Data) + use<'a, E, Ctx>> {
|
||||||
|
if let Some(map) = self.map.get_mut(&event)
|
||||||
|
&& let Some(fs) = map.get(id)
|
||||||
|
{
|
||||||
|
let fs = fs.clone();
|
||||||
|
Some(move |ctx: &mut Ctx, data: E::Data| {
|
||||||
|
for f in &fs {
|
||||||
|
f(ctx, data.clone())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: HashableEvent<Ctx>, Ctx: 'static> DefaultEventModule<E, Ctx> {
|
||||||
|
pub fn run_all(&mut self, ctx: &mut Ctx, event: E, data: E::Data)
|
||||||
|
where
|
||||||
|
E::Data: Clone,
|
||||||
|
{
|
||||||
|
if let Some(map) = self.map.get_mut(&event) {
|
||||||
|
for fs in map.values_mut() {
|
||||||
|
for f in fs {
|
||||||
|
f(ctx, data.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run<W>(&mut self, ctx: &mut Ctx, id: WidgetId<W>, event: E, data: E::Data)
|
||||||
|
where
|
||||||
|
E::Data: Clone,
|
||||||
|
{
|
||||||
|
if let Some(map) = self.map.get_mut(&event)
|
||||||
|
&& let Some(fs) = map.get_mut(&id.id)
|
||||||
|
{
|
||||||
|
for f in fs {
|
||||||
|
f(ctx, data.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Event<Ctx> + 'static, Ctx: 'static> Default for DefaultEventModule<E, Ctx> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
map: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ui {
|
||||||
|
pub fn run_event<E: Event<Ctx>, Ctx: UiCtx, W>(
|
||||||
|
ctx: &mut Ctx,
|
||||||
|
id: &WidgetId<W>,
|
||||||
|
event: E,
|
||||||
|
data: E::Data,
|
||||||
|
) {
|
||||||
|
if let Some(f) = ctx.ui().modules.get_mut::<E::Module>().run(&id.id, event) {
|
||||||
|
f(ctx, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
use ui::{core::CursorState, layout::Vec2};
|
use ui::{
|
||||||
use winit::event::{MouseButton, WindowEvent};
|
core::{CursorState, Modifiers},
|
||||||
|
layout::Vec2,
|
||||||
|
};
|
||||||
|
use winit::{
|
||||||
|
event::{MouseButton, WindowEvent},
|
||||||
|
keyboard::{Key, NamedKey},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::testing::Client;
|
use crate::testing::Client;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Input {
|
pub struct Input {
|
||||||
cursor: CursorState,
|
cursor: CursorState,
|
||||||
|
pub modifiers: Modifiers,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
@@ -27,6 +34,21 @@ impl Input {
|
|||||||
}
|
}
|
||||||
WindowEvent::CursorLeft { .. } => {
|
WindowEvent::CursorLeft { .. } => {
|
||||||
self.cursor.exists = false;
|
self.cursor.exists = false;
|
||||||
|
self.modifiers.clear();
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput { event, .. } => {
|
||||||
|
if let Key::Named(named) = event.logical_key {
|
||||||
|
let pressed = event.state.is_pressed();
|
||||||
|
match named {
|
||||||
|
NamedKey::Control => {
|
||||||
|
self.modifiers.control = pressed;
|
||||||
|
}
|
||||||
|
NamedKey::Shift => {
|
||||||
|
self.modifiers.shift = pressed;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => return false,
|
_ => return false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,13 @@ pub struct Client {
|
|||||||
focus: Option<WidgetId<TextEdit>>,
|
focus: Option<WidgetId<TextEdit>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Hash)]
|
||||||
|
struct Submit;
|
||||||
|
|
||||||
|
impl DefaultEvent for Submit {
|
||||||
|
type Data = ();
|
||||||
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub fn new(window: Arc<Window>) -> Self {
|
pub fn new(window: Arc<Window>) -> Self {
|
||||||
let renderer = Renderer::new(window);
|
let renderer = Renderer::new(window);
|
||||||
@@ -119,7 +126,7 @@ impl Client {
|
|||||||
.span(Dir::DOWN, sized())
|
.span(Dir::DOWN, sized())
|
||||||
.add_static(&mut ui);
|
.add_static(&mut ui);
|
||||||
|
|
||||||
let texts = Span::empty(Dir::DOWN).add(&mut ui);
|
let texts = Span::empty(Dir::DOWN).add_static(&mut ui);
|
||||||
let add_text = text_edit("add")
|
let add_text = text_edit("add")
|
||||||
.text_align(Align::Left)
|
.text_align(Align::Left)
|
||||||
.font_size(30)
|
.font_size(30)
|
||||||
@@ -127,23 +134,26 @@ impl Client {
|
|||||||
client.ui.text(id).select(ctx.cursor, ctx.size);
|
client.ui.text(id).select(ctx.cursor, ctx.size);
|
||||||
client.focus = Some(id.clone());
|
client.focus = Some(id.clone());
|
||||||
})
|
})
|
||||||
|
.id_on(Submit, move |id, client: &mut Client, _| {
|
||||||
|
let content = client.ui.text(id).take();
|
||||||
|
let text = text_edit(content)
|
||||||
|
.font_size(30)
|
||||||
|
.id_on(Sense::click(), |id, client: &mut Client, ctx| {
|
||||||
|
client.ui.text(id).select(ctx.cursor, ctx.size);
|
||||||
|
client.focus = Some(id.clone());
|
||||||
|
})
|
||||||
|
.pad(10)
|
||||||
|
.add(&mut client.ui);
|
||||||
|
client.ui[texts].children.push((text.any(), sized()));
|
||||||
|
})
|
||||||
.add(&mut ui);
|
.add(&mut ui);
|
||||||
let text_edit_scroll = (
|
let text_edit_scroll = (
|
||||||
(Rect::new(Color::SKY), texts.clone()).stack(),
|
(Rect::new(Color::SKY), texts).stack(),
|
||||||
(
|
(
|
||||||
add_text.clone(),
|
add_text.clone(),
|
||||||
Rect::new(Color::GREEN)
|
Rect::new(Color::GREEN)
|
||||||
.on(Sense::click(), move |client: &mut Client, _| {
|
.on(Sense::click(), move |client: &mut Client, _| {
|
||||||
let content = client.ui.text(&add_text).take();
|
Ui::run_event(client, &add_text, Submit, ());
|
||||||
let text = text_edit(content)
|
|
||||||
.font_size(30)
|
|
||||||
.id_on(Sense::click(), |id, client: &mut Client, ctx| {
|
|
||||||
client.ui.text(id).select(ctx.cursor, ctx.size);
|
|
||||||
client.focus = Some(id.clone());
|
|
||||||
})
|
|
||||||
.pad(10)
|
|
||||||
.add(&mut client.ui);
|
|
||||||
client.ui[&texts].children.push((text.any(), sized()));
|
|
||||||
})
|
})
|
||||||
.size(40),
|
.size(40),
|
||||||
)
|
)
|
||||||
@@ -228,9 +238,16 @@ impl Client {
|
|||||||
WindowEvent::KeyboardInput { event, .. } => {
|
WindowEvent::KeyboardInput { event, .. } => {
|
||||||
if let Some(sel) = &self.focus
|
if let Some(sel) = &self.focus
|
||||||
&& event.state.is_pressed()
|
&& event.state.is_pressed()
|
||||||
&& self.ui.text(sel).apply_event(&event).unfocus()
|
|
||||||
{
|
{
|
||||||
self.focus = None;
|
match self.ui.text(sel).apply_event(&event, &self.input.modifiers) {
|
||||||
|
TextInputResult::Unfocus => {
|
||||||
|
self.focus = None;
|
||||||
|
}
|
||||||
|
TextInputResult::Submit => {
|
||||||
|
Ui::run_event(self, &sel.clone(), Submit, ());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
|||||||
Reference in New Issue
Block a user