Compare commits
2 commits
master
...
readable-i
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9daba42f00 | ||
![]() |
05583b892b |
18 changed files with 501 additions and 138 deletions
|
@ -24,12 +24,11 @@ pub(crate) struct State {
|
||||||
pub vel: Vec2,
|
pub vel: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Area {
|
pub struct Area {
|
||||||
id: Id,
|
layer: Layer,
|
||||||
movable: bool,
|
movable: bool,
|
||||||
interactable: bool,
|
interactable: bool,
|
||||||
order: Order,
|
|
||||||
default_pos: Option<Pos2>,
|
default_pos: Option<Pos2>,
|
||||||
fixed_pos: Option<Pos2>,
|
fixed_pos: Option<Pos2>,
|
||||||
}
|
}
|
||||||
|
@ -37,20 +36,19 @@ pub struct Area {
|
||||||
impl Area {
|
impl Area {
|
||||||
pub fn new(id_source: impl Hash) -> Self {
|
pub fn new(id_source: impl Hash) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: Id::new(id_source),
|
layer: Layer {
|
||||||
|
id: Id::new(id_source),
|
||||||
|
order: Order::Middle,
|
||||||
|
},
|
||||||
movable: true,
|
movable: true,
|
||||||
interactable: true,
|
interactable: true,
|
||||||
order: Order::Middle,
|
|
||||||
default_pos: None,
|
default_pos: None,
|
||||||
fixed_pos: None,
|
fixed_pos: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layer(&self) -> Layer {
|
pub fn layer(&self) -> &Layer {
|
||||||
Layer {
|
&self.layer
|
||||||
order: self.order,
|
|
||||||
id: self.id,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// moveable by draggin the area?
|
/// moveable by draggin the area?
|
||||||
|
@ -74,7 +72,7 @@ impl Area {
|
||||||
|
|
||||||
/// `order(Order::Foreground)` for an Area that should always be on top
|
/// `order(Order::Foreground)` for an Area that should always be on top
|
||||||
pub fn order(mut self, order: Order) -> Self {
|
pub fn order(mut self, order: Order) -> Self {
|
||||||
self.order = order;
|
self.layer.order = order;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,19 +99,17 @@ pub(crate) struct Prepared {
|
||||||
impl Area {
|
impl Area {
|
||||||
pub(crate) fn begin(self, ctx: &Arc<Context>) -> Prepared {
|
pub(crate) fn begin(self, ctx: &Arc<Context>) -> Prepared {
|
||||||
let Area {
|
let Area {
|
||||||
id,
|
mut layer,
|
||||||
movable,
|
movable,
|
||||||
order,
|
|
||||||
interactable,
|
interactable,
|
||||||
default_pos,
|
default_pos,
|
||||||
fixed_pos,
|
fixed_pos,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let default_pos = default_pos.unwrap_or_else(|| pos2(100.0, 100.0)); // TODO
|
let default_pos = default_pos.unwrap_or_else(|| pos2(100.0, 100.0)); // TODO
|
||||||
let id = ctx.register_unique_id(id, "Area", default_pos);
|
layer.id = ctx.register_unique_id(layer.id, "Area", default_pos);
|
||||||
let layer = Layer { order, id };
|
|
||||||
|
|
||||||
let mut state = ctx.memory().areas.get(id).unwrap_or_else(|| State {
|
let mut state = ctx.memory().areas.get(&layer.id).unwrap_or_else(|| State {
|
||||||
pos: default_pos,
|
pos: default_pos,
|
||||||
size: Vec2::zero(),
|
size: Vec2::zero(),
|
||||||
interactable,
|
interactable,
|
||||||
|
@ -124,8 +120,7 @@ impl Area {
|
||||||
|
|
||||||
let content_ui = Ui::new(
|
let content_ui = Ui::new(
|
||||||
ctx.clone(),
|
ctx.clone(),
|
||||||
layer,
|
layer.clone(),
|
||||||
id,
|
|
||||||
Rect::from_min_size(state.pos, Vec2::infinity()),
|
Rect::from_min_size(state.pos, Vec2::infinity()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -163,8 +158,13 @@ impl Prepared {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let move_interact =
|
let move_interact = ctx.interact(
|
||||||
ctx.interact(layer, clip_rect, rect, interact_id, Sense::click_and_drag());
|
&layer,
|
||||||
|
clip_rect,
|
||||||
|
rect,
|
||||||
|
interact_id.as_ref(),
|
||||||
|
Sense::click_and_drag(),
|
||||||
|
);
|
||||||
|
|
||||||
let input = ctx.input();
|
let input = ctx.input();
|
||||||
if move_interact.active {
|
if move_interact.active {
|
||||||
|
@ -199,10 +199,10 @@ impl Prepared {
|
||||||
// );
|
// );
|
||||||
|
|
||||||
if move_interact.active
|
if move_interact.active
|
||||||
|| mouse_pressed_on_area(ctx, layer)
|
|| mouse_pressed_on_area(ctx, &layer)
|
||||||
|| !ctx.memory().areas.visible_last_frame(&layer)
|
|| !ctx.memory().areas.visible_last_frame(&layer)
|
||||||
{
|
{
|
||||||
ctx.memory().areas.move_to_top(layer);
|
ctx.memory().areas.move_to_top(&layer);
|
||||||
}
|
}
|
||||||
ctx.memory().areas.set_state(layer, state);
|
ctx.memory().areas.set_state(layer, state);
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ impl Prepared {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_pressed_on_area(ctx: &Context, layer: Layer) -> bool {
|
fn mouse_pressed_on_area(ctx: &Context, layer: &Layer) -> bool {
|
||||||
if let Some(mouse_pos) = ctx.input().mouse.pos {
|
if let Some(mouse_pos) = ctx.input().mouse.pos {
|
||||||
ctx.input().mouse.pressed && ctx.memory().layer_at(mouse_pos) == Some(layer)
|
ctx.input().mouse.pressed && ctx.memory().layer_at(mouse_pos) == Some(layer)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -28,10 +28,10 @@ impl Default for State {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub fn from_memory_with_default_open(ui: &Ui, id: Id, default_open: bool) -> Self {
|
pub fn from_memory_with_default_open(ui: &Ui, id: &Id, default_open: bool) -> Self {
|
||||||
ui.memory()
|
ui.memory()
|
||||||
.collapsing_headers
|
.collapsing_headers
|
||||||
.entry(id)
|
.entry(id.clone())
|
||||||
.or_insert(State {
|
.or_insert(State {
|
||||||
open: default_open,
|
open: default_open,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -40,13 +40,20 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper
|
// Helper
|
||||||
pub fn is_open(ctx: &Context, id: Id) -> Option<bool> {
|
pub fn is_open(ctx: &Context, id: &Id) -> Option<bool> {
|
||||||
ctx.memory()
|
ctx.memory()
|
||||||
.collapsing_headers
|
.collapsing_headers
|
||||||
.get(&id)
|
.get(id)
|
||||||
.map(|state| state.open)
|
.map(|state| state.open)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // Helper
|
||||||
|
// pub fn toggle_by_id(ui: &Ui, id: &Id) {
|
||||||
|
// if let Some(state) = ui.memory().collapsing_headers.get_mut(id) {
|
||||||
|
// state.toggle(ui);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
pub fn toggle(&mut self, ui: &Ui) {
|
pub fn toggle(&mut self, ui: &Ui) {
|
||||||
self.open = !self.open;
|
self.open = !self.open;
|
||||||
self.toggle_time = ui.input().time;
|
self.toggle_time = ui.input().time;
|
||||||
|
@ -196,10 +203,10 @@ impl CollapsingHeader {
|
||||||
);
|
);
|
||||||
|
|
||||||
let rect = ui.allocate_space(size);
|
let rect = ui.allocate_space(size);
|
||||||
let interact = ui.interact(rect, id, Sense::click());
|
let interact = ui.interact(rect, &id, Sense::click());
|
||||||
let text_pos = pos2(text_pos.x, interact.rect.center().y - galley.size.y / 2.0);
|
let text_pos = pos2(text_pos.x, interact.rect.center().y - galley.size.y / 2.0);
|
||||||
|
|
||||||
let mut state = State::from_memory_with_default_open(ui, id, default_open);
|
let mut state = State::from_memory_with_default_open(ui, &id, default_open);
|
||||||
if interact.clicked {
|
if interact.clicked {
|
||||||
state.toggle(ui);
|
state.toggle(ui);
|
||||||
}
|
}
|
||||||
|
@ -241,7 +248,7 @@ impl CollapsingHeader {
|
||||||
|
|
||||||
pub fn show<R>(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> Option<R> {
|
pub fn show<R>(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> Option<R> {
|
||||||
let Prepared { id, mut state } = self.begin(ui);
|
let Prepared { id, mut state } = self.begin(ui);
|
||||||
let r_interact = state.add_contents(ui, |ui| ui.indent(id, add_contents).0);
|
let r_interact = state.add_contents(ui, |ui| ui.indent(&id, add_contents).0);
|
||||||
let ret = r_interact.map(|ri| ri.0);
|
let ret = r_interact.map(|ri| ri.0);
|
||||||
ui.memory().collapsing_headers.insert(id, state);
|
ui.memory().collapsing_headers.insert(id, state);
|
||||||
ret
|
ret
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{widgets::*, *};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, serde_derive::Deserialize, serde_derive::Serialize)]
|
#[derive(Clone, Debug, serde_derive::Deserialize, serde_derive::Serialize)]
|
||||||
pub struct BarState {
|
pub struct BarState {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
open_menu: Option<Id>,
|
open_menu: Option<Id>,
|
||||||
|
@ -53,7 +53,7 @@ fn menu_impl<'c>(
|
||||||
add_contents: Box<dyn FnOnce(&mut Ui) + 'c>,
|
add_contents: Box<dyn FnOnce(&mut Ui) + 'c>,
|
||||||
) {
|
) {
|
||||||
let title = title.into();
|
let title = title.into();
|
||||||
let bar_id = ui.id();
|
let bar_id = ui.id().clone();
|
||||||
let menu_id = Id::new(&title);
|
let menu_id = Id::new(&title);
|
||||||
|
|
||||||
let mut bar_state = ui
|
let mut bar_state = ui
|
||||||
|
@ -65,15 +65,15 @@ fn menu_impl<'c>(
|
||||||
|
|
||||||
let mut button = Button::new(title);
|
let mut button = Button::new(title);
|
||||||
|
|
||||||
if bar_state.open_menu == Some(menu_id) {
|
if bar_state.open_menu.as_ref() == Some(&menu_id) {
|
||||||
button = button.fill(Some(ui.style().interact.active.fill));
|
button = button.fill(Some(ui.style().interact.active.fill));
|
||||||
}
|
}
|
||||||
|
|
||||||
let button_interact = ui.add(button);
|
let button_interact = ui.add(button);
|
||||||
|
|
||||||
interact_with_menu_button(&mut bar_state, ui.input(), menu_id, &button_interact);
|
interact_with_menu_button(&mut bar_state, ui.input(), &menu_id, &button_interact);
|
||||||
|
|
||||||
if bar_state.open_menu == Some(menu_id) {
|
if bar_state.open_menu.as_ref() == Some(&menu_id) {
|
||||||
let area = Area::new(menu_id)
|
let area = Area::new(menu_id)
|
||||||
.order(Order::Foreground)
|
.order(Order::Foreground)
|
||||||
.fixed_pos(button_interact.rect.left_bottom());
|
.fixed_pos(button_interact.rect.left_bottom());
|
||||||
|
@ -110,14 +110,14 @@ fn menu_impl<'c>(
|
||||||
fn interact_with_menu_button(
|
fn interact_with_menu_button(
|
||||||
bar_state: &mut BarState,
|
bar_state: &mut BarState,
|
||||||
input: &InputState,
|
input: &InputState,
|
||||||
menu_id: Id,
|
menu_id: &Id,
|
||||||
button_interact: &GuiResponse,
|
button_interact: &GuiResponse,
|
||||||
) {
|
) {
|
||||||
if button_interact.hovered && input.mouse.pressed {
|
if button_interact.hovered && input.mouse.pressed {
|
||||||
if bar_state.open_menu.is_some() {
|
if bar_state.open_menu.is_some() {
|
||||||
bar_state.open_menu = None;
|
bar_state.open_menu = None;
|
||||||
} else {
|
} else {
|
||||||
bar_state.open_menu = Some(menu_id);
|
bar_state.open_menu = Some(menu_id.clone());
|
||||||
bar_state.open_time = input.time;
|
bar_state.open_time = input.time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ fn interact_with_menu_button(
|
||||||
let time_since_open = input.time - bar_state.open_time;
|
let time_since_open = input.time - bar_state.open_time;
|
||||||
if time_since_open < 0.4 {
|
if time_since_open < 0.4 {
|
||||||
// A quick click
|
// A quick click
|
||||||
bar_state.open_menu = Some(menu_id);
|
bar_state.open_menu = Some(menu_id.clone());
|
||||||
bar_state.open_time = input.time;
|
bar_state.open_time = input.time;
|
||||||
} else {
|
} else {
|
||||||
// A long hold, then release
|
// A long hold, then release
|
||||||
|
@ -135,7 +135,7 @@ fn interact_with_menu_button(
|
||||||
}
|
}
|
||||||
|
|
||||||
if button_interact.hovered && bar_state.open_menu.is_some() {
|
if button_interact.hovered && bar_state.open_menu.is_some() {
|
||||||
bar_state.open_menu = Some(menu_id);
|
bar_state.open_menu = Some(menu_id.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let pressed_escape = input.events.iter().any(|event| {
|
let pressed_escape = input.events.iter().any(|event| {
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub(crate) struct State {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: auto-shink/grow should be part of another container!
|
// TODO: auto-shink/grow should be part of another container!
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Resize {
|
pub struct Resize {
|
||||||
id: Option<Id>,
|
id: Option<Id>,
|
||||||
|
|
||||||
|
@ -178,7 +178,10 @@ struct Prepared {
|
||||||
|
|
||||||
impl Resize {
|
impl Resize {
|
||||||
fn begin(&mut self, ui: &mut Ui) -> Prepared {
|
fn begin(&mut self, ui: &mut Ui) -> Prepared {
|
||||||
let id = self.id.unwrap_or_else(|| ui.make_child_id("resize"));
|
let id = self
|
||||||
|
.id
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| ui.make_child_id("resize"));
|
||||||
self.min_size = self.min_size.min(ui.available().size());
|
self.min_size = self.min_size.min(ui.available().size());
|
||||||
self.max_size = self.max_size.min(ui.available().size());
|
self.max_size = self.max_size.min(ui.available().size());
|
||||||
self.max_size = self.max_size.max(self.min_size);
|
self.max_size = self.max_size.max(self.min_size);
|
||||||
|
@ -209,7 +212,7 @@ impl Resize {
|
||||||
position + state.size + self.handle_offset - corner_size,
|
position + state.size + self.handle_offset - corner_size,
|
||||||
corner_size,
|
corner_size,
|
||||||
);
|
);
|
||||||
let corner_interact = ui.interact(corner_rect, id.with("corner"), Sense::drag());
|
let corner_interact = ui.interact(corner_rect, &id.with("corner"), Sense::drag());
|
||||||
|
|
||||||
if corner_interact.active {
|
if corner_interact.active {
|
||||||
if let Some(mouse_pos) = ui.input().mouse.pos {
|
if let Some(mouse_pos) = ui.input().mouse.pos {
|
||||||
|
|
|
@ -147,7 +147,7 @@ impl Prepared {
|
||||||
|
|
||||||
if content_is_too_small {
|
if content_is_too_small {
|
||||||
// Drag contents to scroll (for touch screens mostly):
|
// Drag contents to scroll (for touch screens mostly):
|
||||||
let content_interact = ui.interact(inner_rect, id.with("area"), Sense::drag());
|
let content_interact = ui.interact(inner_rect, &id.with("area"), Sense::drag());
|
||||||
if content_interact.active {
|
if content_interact.active {
|
||||||
state.offset.y -= ui.input().mouse.delta.y;
|
state.offset.y -= ui.input().mouse.delta.y;
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ impl Prepared {
|
||||||
|
|
||||||
// intentionally use same id for inside and outside of handle
|
// intentionally use same id for inside and outside of handle
|
||||||
let interact_id = id.with("vertical");
|
let interact_id = id.with("vertical");
|
||||||
let mut interact = ui.interact(handle_rect, interact_id, Sense::click_and_drag());
|
let mut interact = ui.interact(handle_rect, &interact_id, Sense::click_and_drag());
|
||||||
|
|
||||||
if let Some(mouse_pos) = ui.input().mouse.pos {
|
if let Some(mouse_pos) = ui.input().mouse.pos {
|
||||||
if interact.active {
|
if interact.active {
|
||||||
|
@ -192,7 +192,7 @@ impl Prepared {
|
||||||
} else {
|
} else {
|
||||||
// Check for mouse down outside handle:
|
// Check for mouse down outside handle:
|
||||||
let scroll_bg_interact =
|
let scroll_bg_interact =
|
||||||
ui.interact(outer_scroll_rect, interact_id, Sense::click_and_drag());
|
ui.interact(outer_scroll_rect, &interact_id, Sense::click_and_drag());
|
||||||
|
|
||||||
if scroll_bg_interact.active {
|
if scroll_bg_interact.active {
|
||||||
// Center scroll at mouse pos:
|
// Center scroll at mouse pos:
|
||||||
|
|
|
@ -149,21 +149,19 @@ impl<'open> Window<'open> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let window_id = Id::new(title_label.text());
|
let window_id = Id::new(title_label.text());
|
||||||
let area_layer = area.layer();
|
|
||||||
let resize_id = window_id.with("resize");
|
let resize_id = window_id.with("resize");
|
||||||
let collapsing_id = window_id.with("collapsing");
|
let collapsing_id = window_id.with("collapsing");
|
||||||
|
|
||||||
let possible = PossibleInteractions {
|
let possible = PossibleInteractions {
|
||||||
movable: area.is_movable(),
|
movable: area.is_movable(),
|
||||||
resizable: resize.is_resizable()
|
resizable: resize.is_resizable()
|
||||||
&& collapsing_header::State::is_open(ctx, collapsing_id).unwrap_or_default(),
|
&& collapsing_header::State::is_open(ctx, &collapsing_id).unwrap_or_default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let area = area.movable(false); // We move it manually
|
let area = area.movable(false); // We move it manually
|
||||||
|
let area_layer = area.layer().clone();
|
||||||
let resize = resize.resizable(false); // We move it manually
|
let resize = resize.resizable(false); // We move it manually
|
||||||
|
let resize = resize.id(resize_id.clone());
|
||||||
let resize = resize.id(resize_id);
|
|
||||||
|
|
||||||
let frame = frame.unwrap_or_else(|| Frame::window(&ctx.style()));
|
let frame = frame.unwrap_or_else(|| Frame::window(&ctx.style()));
|
||||||
|
|
||||||
let mut area = area.begin(ctx);
|
let mut area = area.begin(ctx);
|
||||||
|
@ -174,7 +172,7 @@ impl<'open> Window<'open> {
|
||||||
let default_expanded = true;
|
let default_expanded = true;
|
||||||
let mut collapsing = collapsing_header::State::from_memory_with_default_open(
|
let mut collapsing = collapsing_header::State::from_memory_with_default_open(
|
||||||
&mut frame.content_ui,
|
&mut frame.content_ui,
|
||||||
collapsing_id,
|
&collapsing_id,
|
||||||
default_expanded,
|
default_expanded,
|
||||||
);
|
);
|
||||||
let show_close_button = open.is_some();
|
let show_close_button = open.is_some();
|
||||||
|
@ -182,7 +180,7 @@ impl<'open> Window<'open> {
|
||||||
&mut frame.content_ui,
|
&mut frame.content_ui,
|
||||||
title_label,
|
title_label,
|
||||||
show_close_button,
|
show_close_button,
|
||||||
collapsing_id,
|
&collapsing_id,
|
||||||
&mut collapsing,
|
&mut collapsing,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -208,16 +206,16 @@ impl<'open> Window<'open> {
|
||||||
interact(
|
interact(
|
||||||
ctx,
|
ctx,
|
||||||
possible,
|
possible,
|
||||||
area_layer,
|
&area_layer,
|
||||||
&mut area.state,
|
&mut area.state,
|
||||||
window_id,
|
&window_id,
|
||||||
resize_id,
|
&resize_id,
|
||||||
outer_rect,
|
outer_rect,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let hover_interaction = resize_hover(ctx, possible, area_layer, outer_rect);
|
let hover_interaction = resize_hover(ctx, possible, &area_layer, outer_rect);
|
||||||
|
|
||||||
title_bar.ui(
|
title_bar.ui(
|
||||||
&mut area.content_ui,
|
&mut area.content_ui,
|
||||||
|
@ -264,7 +262,7 @@ struct PossibleInteractions {
|
||||||
resizable: bool,
|
resizable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct WindowInteraction {
|
pub(crate) struct WindowInteraction {
|
||||||
pub(crate) area_layer: Layer,
|
pub(crate) area_layer: Layer,
|
||||||
pub(crate) start_rect: Rect,
|
pub(crate) start_rect: Rect,
|
||||||
|
@ -299,10 +297,10 @@ impl WindowInteraction {
|
||||||
fn interact(
|
fn interact(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
possible: PossibleInteractions,
|
possible: PossibleInteractions,
|
||||||
area_layer: Layer,
|
area_layer: &Layer,
|
||||||
area_state: &mut area::State,
|
area_state: &mut area::State,
|
||||||
window_id: Id,
|
window_id: &Id,
|
||||||
resize_id: Id,
|
resize_id: &Id,
|
||||||
rect: Rect,
|
rect: Rect,
|
||||||
) -> Option<WindowInteraction> {
|
) -> Option<WindowInteraction> {
|
||||||
let pre_resize = ctx.round_rect_to_pixels(rect);
|
let pre_resize = ctx.round_rect_to_pixels(rect);
|
||||||
|
@ -310,7 +308,7 @@ fn interact(
|
||||||
ctx,
|
ctx,
|
||||||
possible,
|
possible,
|
||||||
area_layer,
|
area_layer,
|
||||||
window_id.with("frame_resize"),
|
&window_id.with("frame_resize"),
|
||||||
rect,
|
rect,
|
||||||
)?;
|
)?;
|
||||||
let new_rect = resize_window(ctx, &window_interaction)?;
|
let new_rect = resize_window(ctx, &window_interaction)?;
|
||||||
|
@ -324,9 +322,9 @@ fn interact(
|
||||||
// resize_state.size += new_rect.size() - pre_resize.size();
|
// resize_state.size += new_rect.size() - pre_resize.size();
|
||||||
// resize_state.size = new_rect.size() - some margin;
|
// resize_state.size = new_rect.size() - some margin;
|
||||||
resize_state.requested_size = Some(resize_state.size + new_rect.size() - pre_resize.size());
|
resize_state.requested_size = Some(resize_state.size + new_rect.size() - pre_resize.size());
|
||||||
ctx.memory().resize.insert(resize_id, resize_state);
|
ctx.memory().resize.insert(resize_id.clone(), resize_state);
|
||||||
|
|
||||||
ctx.memory().areas.move_to_top(area_layer);
|
ctx.memory().areas.move_to_top(&area_layer);
|
||||||
Some(window_interaction)
|
Some(window_interaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,14 +356,14 @@ fn resize_window(ctx: &Context, window_interaction: &WindowInteraction) -> Optio
|
||||||
fn window_interaction(
|
fn window_interaction(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
possible: PossibleInteractions,
|
possible: PossibleInteractions,
|
||||||
area_layer: Layer,
|
area_layer: &Layer,
|
||||||
id: Id,
|
id: &Id,
|
||||||
rect: Rect,
|
rect: Rect,
|
||||||
) -> Option<WindowInteraction> {
|
) -> Option<WindowInteraction> {
|
||||||
{
|
{
|
||||||
let drag_id = ctx.memory().interaction.drag_id;
|
let drag_id = ctx.memory().interaction.drag_id.clone();
|
||||||
|
|
||||||
if drag_id.is_some() && drag_id != Some(id) {
|
if drag_id.is_some() && drag_id.as_ref() != Some(id) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -376,17 +374,17 @@ fn window_interaction(
|
||||||
if let Some(hover_window_interaction) = resize_hover(ctx, possible, area_layer, rect) {
|
if let Some(hover_window_interaction) = resize_hover(ctx, possible, area_layer, rect) {
|
||||||
hover_window_interaction.set_cursor(ctx);
|
hover_window_interaction.set_cursor(ctx);
|
||||||
if ctx.input().mouse.pressed {
|
if ctx.input().mouse.pressed {
|
||||||
ctx.memory().interaction.drag_id = Some(id);
|
ctx.memory().interaction.drag_id = Some(id.clone());
|
||||||
window_interaction = Some(hover_window_interaction);
|
window_interaction = Some(hover_window_interaction);
|
||||||
ctx.memory().window_interaction = window_interaction;
|
ctx.memory().window_interaction = window_interaction.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(window_interaction) = window_interaction {
|
if let Some(window_interaction) = window_interaction {
|
||||||
let is_active = ctx.memory().interaction.drag_id == Some(id);
|
let is_active = ctx.memory().interaction.drag_id.as_ref() == Some(id);
|
||||||
|
|
||||||
if is_active && window_interaction.area_layer == area_layer {
|
if is_active && &window_interaction.area_layer == area_layer {
|
||||||
return Some(window_interaction);
|
return Some(window_interaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,7 +395,7 @@ fn window_interaction(
|
||||||
fn resize_hover(
|
fn resize_hover(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
possible: PossibleInteractions,
|
possible: PossibleInteractions,
|
||||||
area_layer: Layer,
|
area_layer: &Layer,
|
||||||
rect: Rect,
|
rect: Rect,
|
||||||
) -> Option<WindowInteraction> {
|
) -> Option<WindowInteraction> {
|
||||||
if let Some(mouse_pos) = ctx.input().mouse.pos {
|
if let Some(mouse_pos) = ctx.input().mouse.pos {
|
||||||
|
@ -451,7 +449,7 @@ fn resize_hover(
|
||||||
|
|
||||||
if any_resize || possible.movable {
|
if any_resize || possible.movable {
|
||||||
Some(WindowInteraction {
|
Some(WindowInteraction {
|
||||||
area_layer,
|
area_layer: area_layer.clone(),
|
||||||
start_rect: rect,
|
start_rect: rect,
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
|
@ -528,7 +526,7 @@ fn show_title_bar(
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
title_label: Label,
|
title_label: Label,
|
||||||
show_close_button: bool,
|
show_close_button: bool,
|
||||||
collapsing_id: Id,
|
collapsing_id: &Id,
|
||||||
collapsing: &mut collapsing_header::State,
|
collapsing: &mut collapsing_header::State,
|
||||||
) -> TitleBar {
|
) -> TitleBar {
|
||||||
let title_bar_and_rect = ui.inner_layout(Layout::horizontal(Align::Center), |ui| {
|
let title_bar_and_rect = ui.inner_layout(Layout::horizontal(Align::Center), |ui| {
|
||||||
|
@ -618,7 +616,7 @@ impl TitleBar {
|
||||||
|
|
||||||
let title_bar_id = ui.make_child_id("title_bar");
|
let title_bar_id = ui.make_child_id("title_bar");
|
||||||
if ui
|
if ui
|
||||||
.interact(self.rect, title_bar_id, Sense::click())
|
.interact(self.rect, &title_bar_id, Sense::click())
|
||||||
.double_clicked
|
.double_clicked
|
||||||
{
|
{
|
||||||
collapsing.toggle(ui);
|
collapsing.toggle(ui);
|
||||||
|
@ -641,7 +639,7 @@ impl TitleBar {
|
||||||
|
|
||||||
fn close_button(ui: &mut Ui, rect: Rect) -> InteractInfo {
|
fn close_button(ui: &mut Ui, rect: Rect) -> InteractInfo {
|
||||||
let close_id = ui.make_child_id("window_close_button");
|
let close_id = ui.make_child_id("window_close_button");
|
||||||
let interact = ui.interact(rect, close_id, Sense::click());
|
let interact = ui.interact(rect, &close_id, Sense::click());
|
||||||
ui.expand_to_include_child(interact.rect);
|
ui.expand_to_include_child(interact.rect);
|
||||||
|
|
||||||
let stroke_color = ui.style().interact(&interact).stroke_color;
|
let stroke_color = ui.style().interact(&interact).stroke_color;
|
||||||
|
|
|
@ -32,10 +32,11 @@ pub struct Context {
|
||||||
output: Mutex<Output>,
|
output: Mutex<Output>,
|
||||||
/// Used to debug name clashes of e.g. windows
|
/// Used to debug name clashes of e.g. windows
|
||||||
used_ids: Mutex<AHashMap<Id, Pos2>>,
|
used_ids: Mutex<AHashMap<Id, Pos2>>,
|
||||||
|
id_interner: Arc<Mutex<crate::id4::IdInterner>>,
|
||||||
paint_stats: Mutex<PaintStats>,
|
paint_stats: Mutex<PaintStats>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need to Clone a Context between each frame
|
||||||
impl Clone for Context {
|
impl Clone for Context {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Context {
|
Context {
|
||||||
|
@ -48,6 +49,7 @@ impl Clone for Context {
|
||||||
graphics: Mutex::new(self.graphics.lock().clone()),
|
graphics: Mutex::new(self.graphics.lock().clone()),
|
||||||
output: Mutex::new(self.output.lock().clone()),
|
output: Mutex::new(self.output.lock().clone()),
|
||||||
used_ids: Mutex::new(self.used_ids.lock().clone()),
|
used_ids: Mutex::new(self.used_ids.lock().clone()),
|
||||||
|
id_interner: Arc::new(Mutex::new(self.id_interner.lock().clone()))
|
||||||
paint_stats: Mutex::new(*self.paint_stats.lock()),
|
paint_stats: Mutex::new(*self.paint_stats.lock()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,6 +69,7 @@ impl Context {
|
||||||
graphics: Default::default(),
|
graphics: Default::default(),
|
||||||
output: Default::default(),
|
output: Default::default(),
|
||||||
used_ids: Default::default(),
|
used_ids: Default::default(),
|
||||||
|
id_interner: Default::default(),
|
||||||
paint_stats: Default::default(),
|
paint_stats: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -204,7 +207,7 @@ impl Context {
|
||||||
};
|
};
|
||||||
// Ensure we register the background area so it is painted:
|
// Ensure we register the background area so it is painted:
|
||||||
self.memory().areas.set_state(
|
self.memory().areas.set_state(
|
||||||
layer,
|
layer.clone(),
|
||||||
containers::area::State {
|
containers::area::State {
|
||||||
pos: rect.min,
|
pos: rect.min,
|
||||||
size: rect.size(),
|
size: rect.size(),
|
||||||
|
@ -212,7 +215,7 @@ impl Context {
|
||||||
vel: Default::default(),
|
vel: Default::default(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
Ui::new(self.clone(), layer, id, rect)
|
Ui::new(self.clone(), layer, rect)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
@ -228,7 +231,7 @@ impl Context {
|
||||||
|
|
||||||
/// If the given Id is not unique, an error will be printed at the given position.
|
/// If the given Id is not unique, an error will be printed at the given position.
|
||||||
pub fn register_unique_id(&self, id: Id, source_name: impl std::fmt::Debug, pos: Pos2) -> Id {
|
pub fn register_unique_id(&self, id: Id, source_name: impl std::fmt::Debug, pos: Pos2) -> Id {
|
||||||
if let Some(clash_pos) = self.used_ids.lock().insert(id, pos) {
|
if let Some(clash_pos) = self.used_ids.lock().insert(id.clone(), pos) {
|
||||||
if clash_pos.distance(pos) < 4.0 {
|
if clash_pos.distance(pos) < 4.0 {
|
||||||
self.show_error(
|
self.show_error(
|
||||||
pos,
|
pos,
|
||||||
|
@ -253,7 +256,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains_mouse(&self, layer: Layer, clip_rect: Rect, rect: Rect) -> bool {
|
pub fn contains_mouse(&self, layer: &Layer, clip_rect: Rect, rect: Rect) -> bool {
|
||||||
let rect = rect.intersect(clip_rect);
|
let rect = rect.intersect(clip_rect);
|
||||||
if let Some(mouse_pos) = self.input.mouse.pos {
|
if let Some(mouse_pos) = self.input.mouse.pos {
|
||||||
rect.contains(mouse_pos) && self.memory().layer_at(mouse_pos) == Some(layer)
|
rect.contains(mouse_pos) && self.memory().layer_at(mouse_pos) == Some(layer)
|
||||||
|
@ -264,10 +267,10 @@ impl Context {
|
||||||
|
|
||||||
pub fn interact(
|
pub fn interact(
|
||||||
&self,
|
&self,
|
||||||
layer: Layer,
|
layer: &Layer,
|
||||||
clip_rect: Rect,
|
clip_rect: Rect,
|
||||||
rect: Rect,
|
rect: Rect,
|
||||||
interaction_id: Option<Id>,
|
interaction_id: Option<&Id>,
|
||||||
sense: Sense,
|
sense: Sense,
|
||||||
) -> InteractInfo {
|
) -> InteractInfo {
|
||||||
let interact_rect = rect.expand2(0.5 * self.style().item_spacing); // make it easier to click. TODO: nice way to do this
|
let interact_rect = rect.expand2(0.5 * self.style().item_spacing); // make it easier to click. TODO: nice way to do this
|
||||||
|
@ -290,8 +293,8 @@ impl Context {
|
||||||
memory.interaction.click_interest |= hovered && sense.click;
|
memory.interaction.click_interest |= hovered && sense.click;
|
||||||
memory.interaction.drag_interest |= hovered && sense.drag;
|
memory.interaction.drag_interest |= hovered && sense.drag;
|
||||||
|
|
||||||
let active = memory.interaction.click_id == Some(interaction_id)
|
let active = memory.interaction.click_id.as_ref() == Some(interaction_id)
|
||||||
|| memory.interaction.drag_id == Some(interaction_id);
|
|| memory.interaction.drag_id.as_ref() == Some(interaction_id);
|
||||||
|
|
||||||
if self.input.mouse.pressed {
|
if self.input.mouse.pressed {
|
||||||
if hovered {
|
if hovered {
|
||||||
|
@ -305,13 +308,13 @@ impl Context {
|
||||||
|
|
||||||
if sense.click && !memory.interaction.click_id.is_some() {
|
if sense.click && !memory.interaction.click_id.is_some() {
|
||||||
// start of a click
|
// start of a click
|
||||||
memory.interaction.click_id = Some(interaction_id);
|
memory.interaction.click_id = Some(interaction_id.clone());
|
||||||
info.active = true;
|
info.active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if sense.drag && !memory.interaction.drag_id.is_some() {
|
if sense.drag && !memory.interaction.drag_id.is_some() {
|
||||||
// start of a drag
|
// start of a drag
|
||||||
memory.interaction.drag_id = Some(interaction_id);
|
memory.interaction.drag_id = Some(interaction_id.clone());
|
||||||
info.active = true;
|
info.active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +368,7 @@ impl Context {
|
||||||
let galley = font.layout_multiline(text, f32::INFINITY);
|
let galley = font.layout_multiline(text, f32::INFINITY);
|
||||||
let rect = align_rect(Rect::from_min_size(pos, galley.size), align);
|
let rect = align_rect(Rect::from_min_size(pos, galley.size), align);
|
||||||
self.add_paint_cmd(
|
self.add_paint_cmd(
|
||||||
layer,
|
&layer,
|
||||||
PaintCmd::Rect {
|
PaintCmd::Rect {
|
||||||
corner_radius: 0.0,
|
corner_radius: 0.0,
|
||||||
fill: Some(color::gray(0, 240)),
|
fill: Some(color::gray(0, 240)),
|
||||||
|
@ -373,7 +376,7 @@ impl Context {
|
||||||
rect: rect.expand(2.0),
|
rect: rect.expand(2.0),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.add_galley(layer, rect.min, galley, text_style, Some(color::RED));
|
self.add_galley(&layer, rect.min, galley, text_style, Some(color::RED));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_text(&self, pos: Pos2, text: impl Into<String>) {
|
pub fn debug_text(&self, pos: Pos2, text: impl Into<String>) {
|
||||||
|
@ -381,7 +384,7 @@ impl Context {
|
||||||
let layer = Layer::debug();
|
let layer = Layer::debug();
|
||||||
let align = (Align::Min, Align::Min);
|
let align = (Align::Min, Align::Min);
|
||||||
self.floating_text(
|
self.floating_text(
|
||||||
layer,
|
&layer,
|
||||||
pos,
|
pos,
|
||||||
text,
|
text,
|
||||||
TextStyle::Monospace,
|
TextStyle::Monospace,
|
||||||
|
@ -394,7 +397,7 @@ impl Context {
|
||||||
let text = text.into();
|
let text = text.into();
|
||||||
let layer = Layer::debug();
|
let layer = Layer::debug();
|
||||||
self.add_paint_cmd(
|
self.add_paint_cmd(
|
||||||
layer,
|
&layer,
|
||||||
PaintCmd::Rect {
|
PaintCmd::Rect {
|
||||||
corner_radius: 0.0,
|
corner_radius: 0.0,
|
||||||
fill: None,
|
fill: None,
|
||||||
|
@ -404,14 +407,14 @@ impl Context {
|
||||||
);
|
);
|
||||||
let align = (Align::Min, Align::Min);
|
let align = (Align::Min, Align::Min);
|
||||||
let text_style = TextStyle::Monospace;
|
let text_style = TextStyle::Monospace;
|
||||||
self.floating_text(layer, rect.min, text, text_style, align, Some(color::RED));
|
self.floating_text(&layer, rect.min, text, text_style, align, Some(color::RED));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show some text anywhere on screen.
|
/// Show some text anywhere on screen.
|
||||||
/// To center the text at the given position, use `align: (Center, Center)`.
|
/// To center the text at the given position, use `align: (Center, Center)`.
|
||||||
pub fn floating_text(
|
pub fn floating_text(
|
||||||
&self,
|
&self,
|
||||||
layer: Layer,
|
layer: &Layer,
|
||||||
pos: Pos2,
|
pos: Pos2,
|
||||||
text: String,
|
text: String,
|
||||||
text_style: TextStyle,
|
text_style: TextStyle,
|
||||||
|
@ -428,7 +431,7 @@ impl Context {
|
||||||
/// Already layed out text.
|
/// Already layed out text.
|
||||||
pub fn add_galley(
|
pub fn add_galley(
|
||||||
&self,
|
&self,
|
||||||
layer: Layer,
|
layer: &Layer,
|
||||||
pos: Pos2,
|
pos: Pos2,
|
||||||
galley: font::Galley,
|
galley: font::Galley,
|
||||||
text_style: TextStyle,
|
text_style: TextStyle,
|
||||||
|
@ -446,7 +449,7 @@ impl Context {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_paint_cmd(&self, layer: Layer, paint_cmd: PaintCmd) {
|
pub fn add_paint_cmd(&self, layer: &Layer, paint_cmd: PaintCmd) {
|
||||||
self.graphics()
|
self.graphics()
|
||||||
.layer(layer)
|
.layer(layer)
|
||||||
.push((Rect::everything(), paint_cmd))
|
.push((Rect::everything(), paint_cmd))
|
||||||
|
|
|
@ -52,7 +52,7 @@ impl Id {
|
||||||
Id(hasher.finish())
|
Id(hasher.finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with(self, child: impl Hash) -> Id {
|
pub fn with(&self, child: impl Hash) -> Id {
|
||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
let mut hasher = ahash::AHasher::default();
|
let mut hasher = ahash::AHasher::default();
|
||||||
hasher.write_u64(self.0);
|
hasher.write_u64(self.0);
|
||||||
|
|
124
emigui/src/id2.rs
Normal file
124
emigui/src/id2.rs
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
use std::{borrow::Cow, sync::Arc};
|
||||||
|
|
||||||
|
use ahash::AHashMap;
|
||||||
|
|
||||||
|
// TODO: is stored Id string with own hash for faster lookups
|
||||||
|
// i.e. pub struct Id(u64, Arc<String>);
|
||||||
|
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub struct Id(Arc<String>);
|
||||||
|
|
||||||
|
impl Id {
|
||||||
|
pub fn as_string(&self) -> &String {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Id {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Id {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// #[derive(Eq, Hash, PartialEq)]
|
||||||
|
// pub enum Component<'a> {
|
||||||
|
// /// E.g. name of a window
|
||||||
|
// String(Cow<'a, str>),
|
||||||
|
|
||||||
|
// /// For loop indices, hashes etc
|
||||||
|
// Int(u64),
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<'a> Component<'a> {
|
||||||
|
// fn to_owned(self) -> Component<'static> {
|
||||||
|
// match self {
|
||||||
|
// Component::String(s) => Component::String(s.into()),
|
||||||
|
// Component::Int(int) => Component::Int(int),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<'a> std::fmt::Debug for Component<'a> {
|
||||||
|
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
// match self {
|
||||||
|
// Component::String(v) => v.fmt(f),
|
||||||
|
// Component::Int(v) => v.fmt(f),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<'a> std::fmt::Display for Component<'a> {
|
||||||
|
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
// match self {
|
||||||
|
// Component::String(v) => v.fmt(f),
|
||||||
|
// Component::Int(v) => v.fmt(f),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
type Generation = u64;
|
||||||
|
|
||||||
|
// One per context
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct IdInterner {
|
||||||
|
/// used to garbage-collect id:s which hasn't been used in a while
|
||||||
|
generation: Generation,
|
||||||
|
|
||||||
|
/// Maps
|
||||||
|
children: AHashMap<(Id, Cow<'static, str>), (Generation, Id)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdInterner {
|
||||||
|
pub fn new_root(&self, root_id: &str) -> Id {
|
||||||
|
Id(Arc::new(root_id.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append `comp` to `parent_id`.
|
||||||
|
/// This is pretty cheap if the same lookup was done last frame,
|
||||||
|
/// else it will cost a memory allocation
|
||||||
|
pub fn child<'a>(&mut self, parent_id: &'a Id, comp: &'a str) -> Id {
|
||||||
|
if let Some(existing) = self.children.get_mut(&(parent_id.clone(), comp.into())) {
|
||||||
|
existing.0 = self.generation;
|
||||||
|
existing.1.clone()
|
||||||
|
} else {
|
||||||
|
let child_id = Id(Arc::new(format!("{}/{}", parent_id, comp)));
|
||||||
|
self.children.insert(
|
||||||
|
(parent_id.clone(), comp.into()),
|
||||||
|
(self.generation, child_id.clone()),
|
||||||
|
);
|
||||||
|
child_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by the context once per frame
|
||||||
|
pub fn gc(&mut self) {
|
||||||
|
let current_gen = self.generation;
|
||||||
|
self.children.retain(|_k, v| v.0 == current_gen);
|
||||||
|
self.generation += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_id() {
|
||||||
|
let interner = parking_lot::Mutex::new(IdInterner::default());
|
||||||
|
let root: Id = interner.lock().new_root("root");
|
||||||
|
let child_a: Id = interner.lock().child(&root, Component::Int(42));
|
||||||
|
let child_b: Id = interner.lock().child(&root, Component::Int(42));
|
||||||
|
|
||||||
|
assert!(root != child_a);
|
||||||
|
assert_eq!(child_a, child_b);
|
||||||
|
}
|
||||||
|
}
|
92
emigui/src/id3.rs
Normal file
92
emigui/src/id3.rs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
use ahash::AHashMap;
|
||||||
|
// use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub struct Id(Arc<String>);
|
||||||
|
|
||||||
|
impl Id {
|
||||||
|
pub fn as_string(&self) -> &String {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Id {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Id {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
type Generation = u64;
|
||||||
|
|
||||||
|
// One per context
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct IdInterner {
|
||||||
|
/// used to garbage-collect id:s which hasn't been used in a while
|
||||||
|
generation: Generation,
|
||||||
|
|
||||||
|
/// Maps
|
||||||
|
children: AHashMap<Id, AHashMap<String, (Generation, Id)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdInterner {
|
||||||
|
pub fn new_root(&self, root_id: &str) -> Id {
|
||||||
|
Id(Arc::new(root_id.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append `comp` to `parent_id`.
|
||||||
|
/// This is pretty cheap if the same lookup was done last frame,
|
||||||
|
/// else it will cost a memory allocation
|
||||||
|
pub fn child(&mut self, parent_id: &Id, comp: &str) -> Id {
|
||||||
|
if let Some(map) = self.children.get_mut(parent_id) {
|
||||||
|
if let Some((gen, child_id)) = map.get_mut(comp) {
|
||||||
|
*gen = self.generation;
|
||||||
|
child_id.clone()
|
||||||
|
} else {
|
||||||
|
let child_id = Id(Arc::new(format!("{}/{}", parent_id, comp)));
|
||||||
|
map.insert(comp.to_owned(), (self.generation, child_id.clone()));
|
||||||
|
child_id
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let child_id = Id(Arc::new(format!("{}/{}", parent_id, comp)));
|
||||||
|
let mut map = AHashMap::new();
|
||||||
|
map.insert(comp.to_owned(), (self.generation, child_id.clone()));
|
||||||
|
self.children.insert(parent_id.clone(), map);
|
||||||
|
child_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by the context once per frame
|
||||||
|
pub fn gc(&mut self) {
|
||||||
|
let current_gen = self.generation;
|
||||||
|
for value in self.children.values_mut() {
|
||||||
|
value.retain(|_comp, (gen, _id)| *gen == current_gen);
|
||||||
|
}
|
||||||
|
self.children.retain(|_k, v| !v.is_empty());
|
||||||
|
self.generation += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_id() {
|
||||||
|
let interner = parking_lot::Mutex::new(IdInterner::default());
|
||||||
|
let root: Id = interner.lock().new_root("root");
|
||||||
|
let child_a: Id = interner.lock().child(&root, "child");
|
||||||
|
let child_b: Id = interner.lock().child(&root, "child");
|
||||||
|
|
||||||
|
assert!(root != child_a);
|
||||||
|
assert_eq!(child_a, child_b);
|
||||||
|
}
|
||||||
|
}
|
135
emigui/src/id4.rs
Normal file
135
emigui/src/id4.rs
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
use ahash::AHashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Id {
|
||||||
|
hash: u64, // precomputed as an optimization
|
||||||
|
string: Arc<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Id {
|
||||||
|
fn from_string(string: String) -> Id {
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
let mut hasher = ahash::AHasher::default();
|
||||||
|
string.hash(&mut hasher);
|
||||||
|
let hash = hasher.finish();
|
||||||
|
Id {
|
||||||
|
hash,
|
||||||
|
string: Arc::new(string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::hash::Hash for Id {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
|
||||||
|
hasher.write_u64(self.hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::cmp::Eq for Id {}
|
||||||
|
|
||||||
|
impl std::cmp::PartialEq for Id {
|
||||||
|
fn eq(&self, other: &Id) -> bool {
|
||||||
|
self.hash == other.hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Id {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.string.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Id {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.string.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl serde::Serialize for Id {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
(*self.string).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> serde::Deserialize<'de> for Id {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Id, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Ok(Id::from_string(serde::Deserialize::deserialize(
|
||||||
|
deserializer,
|
||||||
|
)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
type Generation = u64;
|
||||||
|
|
||||||
|
// One per context
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct IdInterner {
|
||||||
|
/// used to garbage-collect id:s which hasn't been used in a while
|
||||||
|
generation: Generation,
|
||||||
|
|
||||||
|
/// Maps
|
||||||
|
children: AHashMap<Id, AHashMap<String, (Generation, Id)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdInterner {
|
||||||
|
pub fn new_root(&self, root_id: &str) -> Id {
|
||||||
|
Id::from_string(root_id.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append `comp` to `parent_id`.
|
||||||
|
/// This is pretty cheap if the same lookup was done last frame,
|
||||||
|
/// else it will cost a memory allocation
|
||||||
|
pub fn child(&mut self, parent_id: &Id, comp: &str) -> Id {
|
||||||
|
if let Some(map) = self.children.get_mut(parent_id) {
|
||||||
|
if let Some((gen, child_id)) = map.get_mut(comp) {
|
||||||
|
*gen = self.generation;
|
||||||
|
child_id.clone()
|
||||||
|
} else {
|
||||||
|
let child_id = Id::from_string(format!("{}/{}", parent_id, comp));
|
||||||
|
map.insert(comp.to_owned(), (self.generation, child_id.clone()));
|
||||||
|
child_id
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let child_id = Id::from_string(format!("{}/{}", parent_id, comp));
|
||||||
|
let mut map = AHashMap::default();
|
||||||
|
map.insert(comp.to_owned(), (self.generation, child_id.clone()));
|
||||||
|
self.children.insert(parent_id.clone(), map);
|
||||||
|
child_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by the context once per frame
|
||||||
|
pub fn gc(&mut self) {
|
||||||
|
let current_gen = self.generation;
|
||||||
|
for value in self.children.values_mut() {
|
||||||
|
value.retain(|_comp, (gen, _id)| *gen == current_gen);
|
||||||
|
}
|
||||||
|
self.children.retain(|_k, v| !v.is_empty());
|
||||||
|
self.generation += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_id() {
|
||||||
|
let interner = parking_lot::Mutex::new(IdInterner::default());
|
||||||
|
let root: Id = interner.lock().new_root("root");
|
||||||
|
let child_a: Id = interner.lock().child(&root, "child");
|
||||||
|
let child_b: Id = interner.lock().child(&root, "child");
|
||||||
|
|
||||||
|
assert!(root != child_a);
|
||||||
|
assert_eq!(child_a, child_b);
|
||||||
|
assert_eq!(child_a.to_string(), "root/child");
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ pub enum Order {
|
||||||
|
|
||||||
/// An ideintifer for a paint layer.
|
/// An ideintifer for a paint layer.
|
||||||
/// Also acts as an identifier for `Area`:s.
|
/// Also acts as an identifier for `Area`:s.
|
||||||
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Hash, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct Layer {
|
pub struct Layer {
|
||||||
pub order: Order,
|
pub order: Order,
|
||||||
pub id: Id,
|
pub id: Id,
|
||||||
|
@ -41,8 +41,8 @@ type PaintList = Vec<(Rect, PaintCmd)>;
|
||||||
pub struct GraphicLayers(AHashMap<Layer, PaintList>);
|
pub struct GraphicLayers(AHashMap<Layer, PaintList>);
|
||||||
|
|
||||||
impl GraphicLayers {
|
impl GraphicLayers {
|
||||||
pub fn layer(&mut self, layer: Layer) -> &mut PaintList {
|
pub fn layer(&mut self, layer: &Layer) -> &mut PaintList {
|
||||||
self.0.entry(layer).or_default()
|
self.0.entry(layer.clone()).or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drain(
|
pub fn drain(
|
||||||
|
|
|
@ -25,7 +25,8 @@
|
||||||
pub mod containers;
|
pub mod containers;
|
||||||
mod context;
|
mod context;
|
||||||
pub mod examples;
|
pub mod examples;
|
||||||
mod id;
|
// mod id;
|
||||||
|
mod id4;
|
||||||
mod input;
|
mod input;
|
||||||
mod introspection;
|
mod introspection;
|
||||||
mod layers;
|
mod layers;
|
||||||
|
@ -41,7 +42,7 @@ pub mod widgets;
|
||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
context::Context,
|
context::Context,
|
||||||
id::Id,
|
id4::Id,
|
||||||
input::*,
|
input::*,
|
||||||
layers::*,
|
layers::*,
|
||||||
layout::*,
|
layout::*,
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl Memory {
|
||||||
if window_interaction.is_pure_move() {
|
if window_interaction.is_pure_move() {
|
||||||
// Throw windows because it is fun:
|
// Throw windows because it is fun:
|
||||||
let area_layer = window_interaction.area_layer;
|
let area_layer = window_interaction.area_layer;
|
||||||
let area_state = self.areas.get(area_layer.id).clone();
|
let area_state = self.areas.get(&area_layer.id).clone();
|
||||||
if let Some(mut area_state) = area_state {
|
if let Some(mut area_state) = area_state {
|
||||||
area_state.vel = prev_input.mouse.velocity;
|
area_state.vel = prev_input.mouse.velocity;
|
||||||
self.areas.set_state(area_layer, area_state);
|
self.areas.set_state(area_layer, area_state);
|
||||||
|
@ -104,7 +104,7 @@ impl Memory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: call once at the start of the frame for the current mouse pos
|
/// TODO: call once at the start of the frame for the current mouse pos
|
||||||
pub fn layer_at(&self, pos: Pos2) -> Option<Layer> {
|
pub fn layer_at(&self, pos: Pos2) -> Option<&Layer> {
|
||||||
self.areas.layer_at(pos)
|
self.areas.layer_at(pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ impl Areas {
|
||||||
self.areas.len()
|
self.areas.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get(&mut self, id: Id) -> Option<area::State> {
|
pub(crate) fn get(&mut self, id: &Id) -> Option<area::State> {
|
||||||
self.areas.get(&id).cloned()
|
self.areas.get(&id).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,22 +123,22 @@ impl Areas {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_state(&mut self, layer: Layer, state: area::State) {
|
pub(crate) fn set_state(&mut self, layer: Layer, state: area::State) {
|
||||||
self.visible_current_frame.insert(layer);
|
self.visible_current_frame.insert(layer.clone());
|
||||||
let did_insert = self.areas.insert(layer.id, state).is_none();
|
let did_insert = self.areas.insert(layer.id.clone(), state).is_none();
|
||||||
if did_insert {
|
if did_insert {
|
||||||
self.order.push(layer);
|
self.order.push(layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: call once at the start of the frame for the current mouse pos
|
/// TODO: call once at the start of the frame for the current mouse pos
|
||||||
pub fn layer_at(&self, pos: Pos2) -> Option<Layer> {
|
pub fn layer_at(&self, pos: Pos2) -> Option<&Layer> {
|
||||||
for layer in self.order.iter().rev() {
|
for layer in self.order.iter().rev() {
|
||||||
if self.is_visible(layer) {
|
if self.is_visible(layer) {
|
||||||
if let Some(state) = self.areas.get(&layer.id) {
|
if let Some(state) = self.areas.get(&layer.id) {
|
||||||
if state.interactable {
|
if state.interactable {
|
||||||
let rect = Rect::from_min_size(state.pos, state.size);
|
let rect = Rect::from_min_size(state.pos, state.size);
|
||||||
if rect.contains(pos) {
|
if rect.contains(pos) {
|
||||||
return Some(*layer);
|
return Some(&layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,12 +155,12 @@ impl Areas {
|
||||||
self.visible_last_frame.contains(layer) || self.visible_current_frame.contains(layer)
|
self.visible_last_frame.contains(layer) || self.visible_current_frame.contains(layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_to_top(&mut self, layer: Layer) {
|
pub fn move_to_top(&mut self, layer: &Layer) {
|
||||||
self.visible_current_frame.insert(layer);
|
self.visible_current_frame.insert(layer.clone());
|
||||||
self.wants_to_be_on_top.insert(layer);
|
self.wants_to_be_on_top.insert(layer.clone());
|
||||||
|
|
||||||
if self.order.iter().find(|x| **x == layer).is_none() {
|
if self.order.iter().find(|x| *x == layer).is_none() {
|
||||||
self.order.push(layer);
|
self.order.push(layer.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,11 +55,11 @@ impl Ui {
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Creation:
|
// Creation:
|
||||||
|
|
||||||
pub fn new(ctx: Arc<Context>, layer: Layer, id: Id, rect: Rect) -> Self {
|
pub fn new(ctx: Arc<Context>, layer: Layer, rect: Rect) -> Self {
|
||||||
let style = ctx.style();
|
let style = ctx.style();
|
||||||
Ui {
|
Ui {
|
||||||
ctx,
|
ctx,
|
||||||
id,
|
id: layer.id.clone(),
|
||||||
layer,
|
layer,
|
||||||
clip_rect: rect.expand(style.clip_rect_margin),
|
clip_rect: rect.expand(style.clip_rect_margin),
|
||||||
desired_rect: rect,
|
desired_rect: rect,
|
||||||
|
@ -77,8 +77,8 @@ impl Ui {
|
||||||
let clip_rect = self.clip_rect(); // Keep it unless the child explciitly desires differently
|
let clip_rect = self.clip_rect(); // Keep it unless the child explciitly desires differently
|
||||||
Ui {
|
Ui {
|
||||||
ctx: self.ctx.clone(),
|
ctx: self.ctx.clone(),
|
||||||
id: self.id,
|
id: self.id.clone(),
|
||||||
layer: self.layer,
|
layer: self.layer.clone(),
|
||||||
clip_rect,
|
clip_rect,
|
||||||
desired_rect: child_rect,
|
desired_rect: child_rect,
|
||||||
child_bounds: Rect::from_min_size(child_rect.min, Vec2::zero()), // TODO: Rect::nothing() ?
|
child_bounds: Rect::from_min_size(child_rect.min, Vec2::zero()), // TODO: Rect::nothing() ?
|
||||||
|
@ -102,8 +102,8 @@ impl Ui {
|
||||||
self.ctx.round_pos_to_pixels(pos)
|
self.ctx.round_pos_to_pixels(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> Id {
|
pub fn id(&self) -> &Id {
|
||||||
self.id
|
&self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options for this ui, and any child uis we may spawn.
|
/// Options for this ui, and any child uis we may spawn.
|
||||||
|
@ -263,11 +263,11 @@ impl Ui {
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
pub fn contains_mouse(&self, rect: Rect) -> bool {
|
pub fn contains_mouse(&self, rect: Rect) -> bool {
|
||||||
self.ctx.contains_mouse(self.layer, self.clip_rect, rect)
|
self.ctx.contains_mouse(&self.layer, self.clip_rect, rect)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_kb_focus(&self, id: Id) -> bool {
|
pub fn has_kb_focus(&self, id: &Id) -> bool {
|
||||||
self.memory().kb_focus_id == Some(id)
|
self.memory().kb_focus_id.as_ref() == Some(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_kb_focus(&self, id: Id) {
|
pub fn request_kb_focus(&self, id: Id) {
|
||||||
|
@ -303,14 +303,14 @@ impl Ui {
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Interaction
|
// Interaction
|
||||||
|
|
||||||
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> InteractInfo {
|
pub fn interact(&self, rect: Rect, id: &Id, sense: Sense) -> InteractInfo {
|
||||||
self.ctx
|
self.ctx
|
||||||
.interact(self.layer, self.clip_rect, rect, Some(id), sense)
|
.interact(&self.layer, self.clip_rect, rect, Some(id), sense)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interact_hover(&self, rect: Rect) -> InteractInfo {
|
pub fn interact_hover(&self, rect: Rect) -> InteractInfo {
|
||||||
self.ctx
|
self.ctx
|
||||||
.interact(self.layer, self.clip_rect, rect, None, Sense::nothing())
|
.interact(&self.layer, self.clip_rect, rect, None, Sense::nothing())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hovered(&self, rect: Rect) -> bool {
|
pub fn hovered(&self, rect: Rect) -> bool {
|
||||||
|
@ -405,7 +405,7 @@ impl Ui {
|
||||||
pub fn add_paint_cmd(&mut self, paint_cmd: PaintCmd) {
|
pub fn add_paint_cmd(&mut self, paint_cmd: PaintCmd) {
|
||||||
self.ctx
|
self.ctx
|
||||||
.graphics()
|
.graphics()
|
||||||
.layer(self.layer)
|
.layer(&self.layer)
|
||||||
.push((self.clip_rect(), paint_cmd))
|
.push((self.clip_rect(), paint_cmd))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,7 +413,7 @@ impl Ui {
|
||||||
let clip_rect = self.clip_rect();
|
let clip_rect = self.clip_rect();
|
||||||
self.ctx
|
self.ctx
|
||||||
.graphics()
|
.graphics()
|
||||||
.layer(self.layer)
|
.layer(&self.layer)
|
||||||
.extend(cmds.drain(..).map(|cmd| (clip_rect, cmd)));
|
.extend(cmds.drain(..).map(|cmd| (clip_rect, cmd)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,12 +421,12 @@ impl Ui {
|
||||||
pub fn insert_paint_cmd(&mut self, pos: usize, paint_cmd: PaintCmd) {
|
pub fn insert_paint_cmd(&mut self, pos: usize, paint_cmd: PaintCmd) {
|
||||||
self.ctx
|
self.ctx
|
||||||
.graphics()
|
.graphics()
|
||||||
.layer(self.layer)
|
.layer(&self.layer)
|
||||||
.insert(pos, (self.clip_rect(), paint_cmd));
|
.insert(pos, (self.clip_rect(), paint_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paint_list_len(&self) -> usize {
|
pub fn paint_list_len(&self) -> usize {
|
||||||
self.ctx.graphics().layer(self.layer).len()
|
self.ctx.graphics().layer(&self.layer).len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Paint some debug text at current cursor
|
/// Paint some debug text at current cursor
|
||||||
|
|
|
@ -159,7 +159,7 @@ impl Widget for Hyperlink {
|
||||||
let font = &ui.fonts()[text_style];
|
let font = &ui.fonts()[text_style];
|
||||||
let galley = font.layout_multiline(text, ui.available().width());
|
let galley = font.layout_multiline(text, ui.available().width());
|
||||||
let rect = ui.allocate_space(galley.size);
|
let rect = ui.allocate_space(galley.size);
|
||||||
let interact = ui.interact(rect, id, Sense::click());
|
let interact = ui.interact(rect, &id, Sense::click());
|
||||||
if interact.hovered {
|
if interact.hovered {
|
||||||
ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
|
ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
|
||||||
}
|
}
|
||||||
|
@ -241,7 +241,7 @@ impl Widget for Button {
|
||||||
let mut size = galley.size + 2.0 * padding;
|
let mut size = galley.size + 2.0 * padding;
|
||||||
size.y = size.y.max(ui.style().clickable_diameter);
|
size.y = size.y.max(ui.style().clickable_diameter);
|
||||||
let rect = ui.allocate_space(size);
|
let rect = ui.allocate_space(size);
|
||||||
let interact = ui.interact(rect, id, Sense::click());
|
let interact = ui.interact(rect, &id, Sense::click());
|
||||||
let text_cursor = interact.rect.left_center() + vec2(padding.x, -0.5 * galley.size.y);
|
let text_cursor = interact.rect.left_center() + vec2(padding.x, -0.5 * galley.size.y);
|
||||||
let bg_fill = fill.or(ui.style().interact(&interact).bg_fill);
|
let bg_fill = fill.or(ui.style().interact(&interact).bg_fill);
|
||||||
ui.add_paint_cmd(PaintCmd::Rect {
|
ui.add_paint_cmd(PaintCmd::Rect {
|
||||||
|
@ -298,7 +298,7 @@ impl<'a> Widget for Checkbox<'a> {
|
||||||
+ galley.size
|
+ galley.size
|
||||||
+ ui.style().button_padding;
|
+ ui.style().button_padding;
|
||||||
let rect = ui.allocate_space(size);
|
let rect = ui.allocate_space(size);
|
||||||
let interact = ui.interact(rect, id, Sense::click());
|
let interact = ui.interact(rect, &id, Sense::click());
|
||||||
let text_cursor =
|
let text_cursor =
|
||||||
interact.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0);
|
interact.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0);
|
||||||
if interact.clicked {
|
if interact.clicked {
|
||||||
|
@ -377,7 +377,7 @@ impl Widget for RadioButton {
|
||||||
+ galley.size
|
+ galley.size
|
||||||
+ ui.style().button_padding;
|
+ ui.style().button_padding;
|
||||||
let rect = ui.allocate_space(size);
|
let rect = ui.allocate_space(size);
|
||||||
let interact = ui.interact(rect, id, Sense::click());
|
let interact = ui.interact(rect, &id, Sense::click());
|
||||||
let text_cursor =
|
let text_cursor =
|
||||||
interact.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0);
|
interact.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0);
|
||||||
|
|
||||||
|
|
|
@ -138,14 +138,14 @@ impl<'a> Widget for Slider<'a> {
|
||||||
let height = font.line_spacing().max(ui.style().clickable_diameter);
|
let height = font.line_spacing().max(ui.style().clickable_diameter);
|
||||||
let handle_radius = height / 2.5;
|
let handle_radius = height / 2.5;
|
||||||
|
|
||||||
let id = self.id.unwrap_or_else(|| ui.make_position_id());
|
let id = self.id.clone().unwrap_or_else(|| ui.make_position_id());
|
||||||
|
|
||||||
let size = Vec2 {
|
let size = Vec2 {
|
||||||
x: ui.available().width(),
|
x: ui.available().width(),
|
||||||
y: height,
|
y: height,
|
||||||
};
|
};
|
||||||
let rect = ui.allocate_space(size);
|
let rect = ui.allocate_space(size);
|
||||||
let interact = ui.interact(rect, id, Sense::click_and_drag());
|
let interact = ui.interact(rect, &id, Sense::click_and_drag());
|
||||||
|
|
||||||
let left = interact.rect.left() + handle_radius;
|
let left = interact.rect.left() + handle_radius;
|
||||||
let right = interact.rect.right() - handle_radius;
|
let right = interact.rect.right() - handle_radius;
|
||||||
|
|
|
@ -72,10 +72,10 @@ impl<'t> Widget for TextEdit<'t> {
|
||||||
};
|
};
|
||||||
let desired_size = galley.size.max(vec2(available_width, line_spacing));
|
let desired_size = galley.size.max(vec2(available_width, line_spacing));
|
||||||
let rect = ui.allocate_space(desired_size);
|
let rect = ui.allocate_space(desired_size);
|
||||||
let interact = ui.interact(rect, id, Sense::click_and_drag()); // TODO: implement drag-select
|
let interact = ui.interact(rect, &id, Sense::click_and_drag()); // TODO: implement drag-select
|
||||||
|
|
||||||
if interact.clicked {
|
if interact.clicked {
|
||||||
ui.request_kb_focus(id);
|
ui.request_kb_focus(id.clone());
|
||||||
if let Some(mouse_pos) = ui.input().mouse.pos {
|
if let Some(mouse_pos) = ui.input().mouse.pos {
|
||||||
state.cursor = Some(galley.char_at(mouse_pos - interact.rect.min).char_idx);
|
state.cursor = Some(galley.char_at(mouse_pos - interact.rect.min).char_idx);
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ impl<'t> Widget for TextEdit<'t> {
|
||||||
if interact.hovered {
|
if interact.hovered {
|
||||||
ui.output().cursor_icon = CursorIcon::Text;
|
ui.output().cursor_icon = CursorIcon::Text;
|
||||||
}
|
}
|
||||||
let has_kb_focus = ui.has_kb_focus(id);
|
let has_kb_focus = ui.has_kb_focus(&id);
|
||||||
|
|
||||||
if has_kb_focus {
|
if has_kb_focus {
|
||||||
let mut cursor = state.cursor.unwrap_or_else(|| text.chars().count());
|
let mut cursor = state.cursor.unwrap_or_else(|| text.chars().count());
|
||||||
|
|
Loading…
Reference in a new issue