view + a bunch of fixes or smth idek man

This commit is contained in:
2025-12-08 00:12:11 -05:00
parent b66d4da5d7
commit 434e3c3af7
18 changed files with 253 additions and 109 deletions

View File

@@ -1,8 +1,5 @@
use cosmic_text::Family;
use iris::{
prelude::*,
winit::{attr::Selectable, event::Submit, *},
};
use iris::prelude::*;
use len_fns::*;
use winit::event::WindowEvent;
@@ -109,7 +106,7 @@ impl DefaultAppState for Client {
.text_align(Align::LEFT)
.size(30)
.attr::<Selectable>(())
.on(Submit, move |ctx| {
.on(iris::winit::Submit, move |ctx| {
let content = ctx.widget.get_mut().take();
let text = wtext(content)
.editable(false)

View File

@@ -11,7 +11,7 @@ use crate::{
};
#[derive(Clone, Copy, PartialEq)]
pub enum Button {
pub enum CursorButton {
Left,
Right,
Middle,
@@ -19,9 +19,9 @@ pub enum Button {
#[derive(Clone, Copy, PartialEq)]
pub enum CursorSense {
PressStart(Button),
Pressing(Button),
PressEnd(Button),
PressStart(CursorButton),
Pressing(CursorButton),
PressEnd(CursorButton),
HoverStart,
Hovering,
HoverEnd,
@@ -32,16 +32,16 @@ pub struct CursorSenses(Vec<CursorSense>);
impl CursorSense {
pub fn click() -> Self {
Self::PressStart(Button::Left)
Self::PressStart(CursorButton::Left)
}
pub fn click_or_drag() -> CursorSenses {
Self::click() | Self::Pressing(Button::Left)
Self::click() | Self::Pressing(CursorButton::Left)
}
pub fn unclick() -> Self {
Self::PressEnd(Button::Left)
Self::PressEnd(CursorButton::Left)
}
pub fn is_dragging(&self) -> bool {
matches!(self, CursorSense::Pressing(Button::Left))
matches!(self, CursorSense::Pressing(CursorButton::Left))
}
}
@@ -61,11 +61,11 @@ pub struct CursorButtons {
}
impl CursorButtons {
pub fn select(&self, button: &Button) -> &ActivationState {
pub fn select(&self, button: &CursorButton) -> &ActivationState {
match button {
Button::Left => &self.left,
Button::Right => &self.right,
Button::Middle => &self.middle,
CursorButton::Left => &self.left,
CursorButton::Right => &self.right,
CursorButton::Middle => &self.middle,
}
}
@@ -205,7 +205,11 @@ impl<Ctx: 'static> CursorModule<Ctx> {
scroll_delta: cursor.scroll_delta,
sense,
};
(sensor.f)(EventCtx { ui, state: ctx, data });
(sensor.f)(EventCtx {
ui,
state: ctx,
data,
});
}
}
}

View File

@@ -75,7 +75,7 @@ impl TextBuilderOutput for TextOutput {
builder.attrs.line_height,
));
let hint = builder.hint.get(ui);
let font_system = &mut ui.data.text.borrow_mut().font_system;
let font_system = &mut ui.data.text.get_mut().font_system;
buf.set_text(font_system, &builder.content, &Attrs::new(), SHAPING, None);
let mut text = Text {
content: builder.content.into(),
@@ -103,7 +103,7 @@ impl TextBuilderOutput for TextEditOutput {
builder.output.single_line,
ui.data.text.clone(),
);
let font_system = &mut ui.data.text.borrow_mut().font_system;
let font_system = &mut ui.data.text.get_mut().font_system;
text.buf
.set_text(font_system, &builder.content, &Attrs::new(), SHAPING, None);
builder.attrs.apply(font_system, &mut text.buf, None);

View File

@@ -60,7 +60,7 @@ impl TextEdit {
pub fn set(&mut self, text: &str) {
let text = self.string(text);
self.view.buf.set_text(
&mut self.data.borrow_mut().font_system,
&mut self.data.get_mut().font_system,
&text,
&Attrs::new(),
SHAPING,
@@ -238,7 +238,7 @@ impl TextEdit {
self.view
.buf
.cursor_motion(
&mut self.data.borrow_mut().font_system,
&mut self.data.get_mut().font_system,
cursor,
None,
motion,

View File

@@ -67,7 +67,7 @@ impl TextView {
return tex.clone();
}
self.width = width;
let mut text_data = ctx.text.borrow_mut();
let mut text_data = ctx.text.get_mut();
self.attrs
.apply(&mut text_data.font_system, &mut self.buf, width);
self.buf
@@ -139,7 +139,7 @@ impl Text {
if self.content.changed {
self.content.changed = false;
self.view.buf.set_text(
&mut ctx.text.borrow_mut().font_system,
&mut ctx.text.get_mut().font_system,
&self.content,
&Attrs::new().family(self.view.attrs.family),
SHAPING,

View File

@@ -1,5 +1,7 @@
#![allow(clippy::multiple_bound_locations)]
use std::marker::Destruct;
/// stored in linear for sane manipulation
#[repr(C)]
#[derive(Clone, Copy, Hash, PartialEq, Eq, bytemuck::Zeroable, Debug)]
@@ -56,23 +58,47 @@ pub trait ColorNum {
const MAX: Self;
}
impl<T: ColorNum + F32Conversion> Color<T> {
pub fn mul_rgb(self, amt: impl F32Conversion) -> Self {
macro_rules! map_rgb {
($x:ident,$self:ident, $e:tt) => {
#[allow(unused_braces)]
Self {
r: {
let $x = $self.r;
$e
},
g: {
let $x = $self.g;
$e
},
b: {
let $x = $self.b;
$e
},
a: $self.a,
}
};
}
impl<T: ColorNum + const F32Conversion> Color<T>
where
Self: const Destruct,
{
pub const fn mul_rgb(self, amt: impl const F32Conversion) -> Self {
let amt = amt.to();
self.map_rgb(|x| T::from(x.to() * amt))
map_rgb!(x, self, { T::from(x.to() * amt) })
}
pub fn add_rgb(self, amt: impl F32Conversion) -> Self {
pub const fn add_rgb(self, amt: impl const F32Conversion) -> Self {
let amt = amt.to();
self.map_rgb(|x| T::from(x.to() + amt))
map_rgb!(x, self, { T::from(x.to() + amt) })
}
pub fn darker(self, amt: f32) -> Self {
pub const fn darker(self, amt: f32) -> Self {
self.mul_rgb(1.0 - amt)
}
pub fn brighter(self, amt: f32) -> Self {
self.map_rgb(|x| {
pub const fn brighter(self, amt: f32) -> Self {
map_rgb!(x, self, {
let x = x.to();
T::from(x + (1.0 - x) * amt)
})

View File

@@ -28,7 +28,10 @@ pub trait EventFn<Ctx, Data>: Fn(EventCtx<Ctx, Data>) + 'static {}
impl<F: Fn(EventCtx<Ctx, Data>) + 'static, Ctx, Data> EventFn<Ctx, Data> for F {}
pub trait WidgetEventFn<Ctx, Data, W: ?Sized>: Fn(EventIdCtx<Ctx, Data, W>) + 'static {}
impl<F: Fn(EventIdCtx<Ctx, Data, W>) + 'static, Ctx, Data, W> WidgetEventFn<Ctx, Data, W> for F {}
impl<F: Fn(EventIdCtx<Ctx, Data, W>) + 'static, Ctx, Data, W: ?Sized> WidgetEventFn<Ctx, Data, W>
for F
{
}
// TODO: naming in here is a bit weird like eventable
#[macro_export]
@@ -40,6 +43,7 @@ macro_rules! event_ctx {
#[allow(unused_imports)]
use $crate::prelude::*;
#[allow(unused)]
pub trait EventableCtx<W: ?Sized, Tag, Ctx: 'static> {
fn on<E: Event>(
self,
@@ -57,6 +61,33 @@ macro_rules! event_ctx {
eventable::Eventable::on(self, event, f)
}
}
#[allow(unused)]
pub trait EventableCtxUi<W: ?Sized, Tag, Ctx: 'static>
where
WidgetRef<W>: EventableCtx<W, Tag, Ctx>,
{
fn on<E: Event>(
&mut self,
widget: &WidgetRef<W>,
event: E,
f: impl WidgetEventFn<Ctx, E::Data, W>,
);
}
impl<W: ?Sized + 'static, Tag> EventableCtxUi<W, Tag, $ty> for Ui
where
WidgetRef<W>: EventableCtx<W, Tag, $ty>,
{
fn on<E: Event>(
&mut self,
widget: &WidgetRef<W>,
event: E,
f: impl WidgetEventFn<$ty, E::Data, W>,
) {
self.register_widget_event(&widget, event, f);
}
}
}
use local_event_trait::*;
};
@@ -82,21 +113,47 @@ pub mod eventable {
) -> impl WidgetIdFn<WL::Widget> {
move |ui| {
let id = self.add(ui);
let id_ = id.weak();
ui.register_event(&id, event, move |ctx| {
f(EventIdCtx {
widget: &id_.expect_strong(),
state: ctx.state,
data: ctx.data,
ui: ctx.ui,
});
});
ui.register_widget_event(&id, event, f);
id
}
}
}
}
impl Ui {
pub fn register_event<W: ?Sized, E: Event, Ctx: 'static>(
&mut self,
id: &WidgetRef<W>,
event: E,
f: impl EventFn<Ctx, E::Data>,
) {
self.data
.modules
.get_mut::<E::Module<Ctx>>()
.register(id.id(), event, f);
}
pub fn register_widget_event<W: ?Sized + 'static, E: Event, Ctx: 'static>(
&mut self,
id: &WidgetRef<W>,
event: E,
f: impl WidgetEventFn<Ctx, E::Data, W>,
) {
let id_ = id.weak();
self.data
.modules
.get_mut::<E::Module<Ctx>>()
.register(id.id(), event, move |ctx| {
f(EventIdCtx {
widget: &id_.expect_strong(),
ui: ctx.ui,
state: ctx.state,
data: ctx.data,
})
});
}
}
pub trait DefaultEvent: Hash + Eq + 'static {
type Data: Clone;
}
@@ -193,7 +250,7 @@ impl<E: Event + 'static, Ctx: 'static> Default for DefaultEventModule<E, Ctx> {
}
impl Ui {
pub fn run_event<E: Event, Ctx: 'static, W>(
pub fn run_event<E: Event, Ctx: 'static, W: ?Sized>(
&mut self,
ctx: &mut Ctx,
id: &WidgetRef<W>,

View File

@@ -9,6 +9,7 @@ mod painter;
mod text;
mod texture;
mod ui;
mod view;
mod widget;
mod widget_ref;
mod widgets;
@@ -24,6 +25,7 @@ pub use painter::*;
pub use text::*;
pub use texture::*;
pub use ui::*;
pub use view::*;
pub use widget::*;
pub use widget_ref::*;
pub use widgets::*;

View File

@@ -450,7 +450,7 @@ impl<'a, 'c> Painter<'a, 'c> {
pub fn render_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> RenderedText {
self.ctx
.text
.borrow_mut()
.get_mut()
.draw(buffer, attrs, self.ctx.textures)
}
@@ -604,6 +604,6 @@ impl SizeCtx<'_> {
}
pub fn draw_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> RenderedText {
self.text.borrow_mut().draw(buffer, attrs, self.textures)
self.text.get_mut().draw(buffer, attrs, self.textures)
}
}

View File

@@ -1,10 +1,9 @@
use std::{
cell::RefCell,
rc::Rc,
simd::{Simd, num::SimdUint},
};
use std::simd::{Simd, num::SimdUint};
use crate::layout::{Align, RegionAlign, TextureHandle, Textures, UiColor, Vec2};
use crate::{
layout::{Align, RegionAlign, TextureHandle, Textures, UiColor, Vec2},
util::Handle,
};
use cosmic_text::{
Attrs, AttrsList, Buffer, CacheKey, Color, Family, FontSystem, Metrics, Placement, SwashCache,
SwashContent,
@@ -16,7 +15,7 @@ pub mod text_lib {
pub use cosmic_text::*;
}
pub type TextData = Rc<RefCell<TextDataInner>>;
pub type TextData = Handle<TextDataInner>;
pub struct TextDataInner {
pub font_system: FontSystem,

View File

@@ -3,7 +3,7 @@ use image::DynamicImage;
use crate::{
layout::{
Event, EventFn, EventModule, IdLike, PainterData, PixelRegion, TextureHandle, Vec2, Widget,
WidgetInstance, WidgetLike, WidgetRef, WidgetUpdate,
WidgetEventFn, WidgetInstance, WidgetLike, WidgetRef, WidgetUpdate,
},
util::{HashSet, Id},
};
@@ -42,18 +42,6 @@ impl Ui {
self.data.textures.add(image)
}
pub fn register_event<W: ?Sized, E: Event, Ctx: 'static>(
&mut self,
id: &WidgetRef<W>,
event: E,
f: impl EventFn<Ctx, E::Data>,
) {
self.data
.modules
.get_mut::<E::Module<Ctx>>()
.register(id.id(), event, f);
}
pub fn resize(&mut self, size: impl Into<Vec2>) {
self.data.output_size = size.into();
self.resized = true;

17
src/layout/view.rs Normal file
View File

@@ -0,0 +1,17 @@
use crate::layout::{Widget, WidgetLike, WidgetRef};
use std::marker::Unsize;
pub trait WidgetView {
type Widget: Widget + ?Sized + Unsize<dyn Widget> = dyn Widget;
fn view(&self) -> &WidgetRef<Self::Widget>;
}
pub struct ViewTag;
impl<WV: WidgetView> WidgetLike<ViewTag> for WV {
type Widget = WV::Widget;
fn add(self, _ui: &mut super::Ui) -> WidgetRef<Self::Widget> {
self.view().clone()
}
}

View File

@@ -61,7 +61,7 @@ pub trait WidgetLike<Tag> {
/// A function that returns a widget given a UI.
/// Useful for defining trait functions on widgets that create a parent widget so that the children
/// don't need to be IDs yet
pub trait WidgetFn<W: Widget>: FnOnce(&mut Ui) -> W {}
pub trait WidgetFn<W: Widget + ?Sized>: FnOnce(&mut Ui) -> W {}
impl<W: Widget, F: FnOnce(&mut Ui) -> W> WidgetFn<W> for F {}
impl<W: Widget, F: FnOnce(&mut Ui) -> W> WidgetLike<FnTag> for F {

View File

@@ -1,24 +1,20 @@
use std::{
cell::{Ref, RefCell, RefMut},
cell::{Ref, RefMut},
marker::Unsize,
rc::{Rc, Weak},
rc::Rc,
sync::mpsc::Sender,
};
use crate::{
layout::{Ui, Widget, WidgetLike},
util::Id,
util::{Handle, Id, WeakHandle},
};
/// An identifier for a widget that can index a UI to get the associated widget.
/// It should always remain valid; it keeps a ref count and removes the widget from the UI if all
/// references are dropped.
///
/// W does not need to implement widget so that AnyWidget is valid;
/// Instead, add generic bounds on methods that take an ID if they need specific data.
/// An handle for a widget in a UI.
///
/// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs?
pub struct WidgetRef<W: ?Sized = dyn Widget>(Rc<RefCell<Inner<W>>>);
pub struct WidgetRef<W: ?Sized = dyn Widget>(Handle<Inner<W>>);
pub struct WeakWidgetRef<W: ?Sized = dyn Widget>(WeakHandle<Inner<W>>);
struct Inner<W: ?Sized> {
id: Id,
@@ -32,8 +28,6 @@ pub enum WidgetUpdate {
Mutate(Id),
}
pub struct WeakWidgetRef<W: ?Sized = dyn Widget>(Weak<RefCell<Inner<W>>>);
impl<W> PartialEq for WidgetRef<W> {
fn eq(&self, other: &Self) -> bool {
self.id() == other.id()
@@ -58,12 +52,15 @@ impl<W: Widget> WidgetRef<W> {
if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) {
label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1;
}
Self(Rc::new(RefCell::new(Inner {
Self(
Inner {
widget,
id,
send,
label,
})))
}
.into(),
)
}
}
@@ -78,44 +75,44 @@ impl<W: Widget + ?Sized + Unsize<dyn Widget>> WidgetRef<W> {
impl<W: ?Sized> WidgetRef<W> {
pub fn id(&self) -> Id {
self.0.borrow().id
self.0.get().id
}
pub fn get(&self) -> Ref<'_, W> {
Ref::map(self.0.borrow(), |i| &i.widget)
Ref::map(self.0.get(), |i| &i.widget)
}
pub fn get_mut(&self) -> RefMut<'_, W> {
let inner = self.0.borrow_mut();
let inner = self.0.get_mut();
let _ = inner.send.send(WidgetUpdate::Mutate(inner.id));
RefMut::map(inner, |i| &mut i.widget)
}
pub fn get_mut_quiet(&self) -> RefMut<'_, W> {
RefMut::map(self.0.borrow_mut(), |i| &mut i.widget)
RefMut::map(self.0.get_mut(), |i| &mut i.widget)
}
pub fn get_label(&self) -> Ref<'_, String> {
Ref::map(self.0.borrow(), |i| &i.label)
Ref::map(self.0.get(), |i| &i.label)
}
pub fn set_label(&self, label: impl Into<String>) {
self.0.borrow_mut().label = label.into();
self.0.get_mut().label = label.into();
}
pub fn refs(&self) -> usize {
Rc::strong_count(&self.0)
self.0.refs()
}
pub fn weak(&self) -> WeakWidgetRef<W> {
WeakWidgetRef(Rc::downgrade(&self.0))
WeakWidgetRef(self.0.weak())
}
}
impl<W: ?Sized> WeakWidgetRef<W> {
/// should guarantee that widget is still valid to prevent indexing failures
pub(crate) fn expect_strong(&self) -> WidgetRef<W> {
WidgetRef(self.0.upgrade().expect("widget should not be dropped"))
WidgetRef(self.0.strong().expect("widget should not be dropped"))
}
}
@@ -156,16 +153,6 @@ impl<W: Widget + ?Sized + Unsize<dyn Widget> + 'static, F: FnOnce(&mut Ui) -> Wi
}
}
pub trait WidgetIdLike<W> {
fn rf(self, send: &Sender<Id>) -> WidgetRef<W>;
}
impl<W> WidgetIdLike<W> for &WidgetRef<W> {
fn rf(self, _: &Sender<Id>) -> WidgetRef<W> {
self.clone()
}
}
pub trait IdLike<W> {
fn id(&self) -> Id;
}

View File

@@ -11,6 +11,7 @@
#![feature(gen_blocks)]
#![feature(associated_type_defaults)]
#![feature(unsize)]
#![feature(coerce_unsized)]
pub mod core;
pub mod layout;
@@ -22,4 +23,6 @@ pub mod prelude {
pub use crate::core::*;
pub use crate::layout::*;
pub use crate::render::*;
pub use crate::util::Handle;
pub use crate::winit::*;
}

60
src/util/handle.rs Normal file
View File

@@ -0,0 +1,60 @@
use std::{
cell::{Ref, RefCell, RefMut},
marker::Unsize,
ops::CoerceUnsized,
rc::{Rc, Weak},
};
pub struct Handle<T: ?Sized>(Rc<RefCell<T>>);
pub struct WeakHandle<T: ?Sized>(Weak<RefCell<T>>);
impl<T: ?Sized> Handle<T> {
pub fn get(&self) -> Ref<T> {
self.0.borrow()
}
pub fn get_mut(&self) -> RefMut<'_, T> {
self.0.borrow_mut()
}
pub fn refs(&self) -> usize {
Rc::strong_count(&self.0)
}
pub fn weak(&self) -> WeakHandle<T> {
WeakHandle(Rc::downgrade(&self.0))
}
}
impl<T: ?Sized> WeakHandle<T> {
pub fn strong(&self) -> Option<Handle<T>> {
Some(Handle(self.0.upgrade()?))
}
}
impl<T: ?Sized> Clone for Handle<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: ?Sized> Clone for WeakHandle<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: ?Sized + Default> Default for Handle<T> {
fn default() -> Self {
Self(Default::default())
}
}
impl<T> From<T> for Handle<T> {
fn from(value: T) -> Self {
Self(Rc::new(RefCell::new(value)))
}
}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Handle<U>> for Handle<T> {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<WeakHandle<U>> for WeakHandle<T> {}

View File

@@ -4,6 +4,7 @@ mod id;
mod math;
mod refcount;
mod vec2;
mod handle;
pub(crate) use arena::*;
pub use change::*;
@@ -11,6 +12,7 @@ pub(crate) use id::*;
pub(crate) use math::*;
pub(crate) use refcount::*;
pub use vec2::*;
pub use handle::*;
pub type HashMap<K, V> = fxhash::FxHashMap<K, V>;
pub type HashSet<K> = fxhash::FxHashSet<K>;

View File

@@ -1,6 +1,4 @@
use crate::prelude::*;
use crate::winit::event::{Edited, Submit};
use crate::winit::{input::Input, render::UiRenderer};
use arboard::Clipboard;
use std::sync::Arc;
use std::time::Instant;
@@ -9,12 +7,16 @@ use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
use winit::window::{Window, WindowAttributes};
mod app;
pub mod attr;
pub mod event;
pub mod input;
pub mod render;
mod attr;
mod event;
mod input;
mod render;
pub use app::*;
pub use attr::*;
pub use event::*;
pub use input::*;
pub use render::*;
pub type Proxy<Event> = EventLoopProxy<Event>;
pub type DefaultApp<Data> = App<DefaultState<Data>>;