2020-04-25 20:49:57 +00:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
use crate::{widgets::*, *};
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
2020-04-26 20:30:24 +00:00
|
|
|
/// A wrapper around other containers for things you often want in a window
|
2020-05-10 17:02:34 +00:00
|
|
|
pub struct Window<'open> {
|
2020-05-08 15:16:32 +00:00
|
|
|
pub title_label: Label,
|
2020-05-10 17:02:34 +00:00
|
|
|
open: Option<&'open mut bool>,
|
2020-05-10 11:14:52 +00:00
|
|
|
pub area: Area,
|
2020-05-10 11:06:16 +00:00
|
|
|
pub frame: Option<Frame>,
|
2020-05-01 00:08:01 +00:00
|
|
|
pub resize: Resize,
|
2020-05-10 17:00:48 +00:00
|
|
|
pub scroll: Option<ScrollArea>,
|
2020-04-25 20:49:57 +00:00
|
|
|
}
|
|
|
|
|
2020-05-10 17:02:34 +00:00
|
|
|
impl<'open> Window<'open> {
|
2020-05-08 15:16:32 +00:00
|
|
|
// TODO: Into<Label>
|
2020-04-25 20:49:57 +00:00
|
|
|
pub fn new(title: impl Into<String>) -> Self {
|
|
|
|
let title = title.into();
|
2020-05-10 11:14:52 +00:00
|
|
|
let area = Area::new(&title);
|
2020-05-08 15:16:32 +00:00
|
|
|
let title_label = Label::new(title)
|
|
|
|
.text_style(TextStyle::Heading)
|
|
|
|
.multiline(false);
|
2020-04-25 20:49:57 +00:00
|
|
|
Self {
|
2020-05-08 15:16:32 +00:00
|
|
|
title_label,
|
2020-05-10 17:02:34 +00:00
|
|
|
open: None,
|
2020-05-10 11:14:52 +00:00
|
|
|
area,
|
2020-05-10 11:06:16 +00:00
|
|
|
frame: None,
|
2020-04-25 20:49:57 +00:00
|
|
|
resize: Resize::default()
|
2020-05-17 07:44:09 +00:00
|
|
|
.auto_expand_height(false)
|
2020-05-01 00:08:01 +00:00
|
|
|
.auto_expand_width(true)
|
2020-04-26 20:30:24 +00:00
|
|
|
.auto_shrink_height(false)
|
2020-05-17 07:44:09 +00:00
|
|
|
.auto_shrink_width(true)
|
|
|
|
.handle_offset(Vec2::splat(4.0))
|
|
|
|
.outline(false),
|
2020-05-10 17:00:48 +00:00
|
|
|
scroll: Some(
|
|
|
|
ScrollArea::default()
|
2020-05-10 17:02:34 +00:00
|
|
|
.always_show_scroll(false)
|
2020-05-10 17:00:48 +00:00
|
|
|
.max_height(f32::INFINITY),
|
|
|
|
), // As large as we can be
|
2020-04-25 20:49:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-10 17:02:34 +00:00
|
|
|
/// If the given bool is false, the window will not be visible.
|
|
|
|
/// If the given bool is true, the window will have a close button that sets this bool to false.
|
|
|
|
pub fn open(mut self, open: &'open mut bool) -> Self {
|
|
|
|
self.open = Some(open);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-05-01 00:08:01 +00:00
|
|
|
/// Usage: `Winmdow::new(...).mutate(|w| w.resize = w.resize.auto_expand_width(true))`
|
2020-05-11 18:21:24 +00:00
|
|
|
/// Not sure this is a good interface for this.
|
2020-05-01 00:08:01 +00:00
|
|
|
pub fn mutate(mut self, mutate: impl Fn(&mut Self)) -> Self {
|
|
|
|
mutate(&mut self);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Usage: `Winmdow::new(...).resize(|r| r.auto_expand_width(true))`
|
2020-05-11 18:21:24 +00:00
|
|
|
/// Not sure this is a good interface for this.
|
2020-05-01 00:08:01 +00:00
|
|
|
pub fn resize(mut self, mutate: impl Fn(Resize) -> Resize) -> Self {
|
|
|
|
self.resize = mutate(self.resize);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-05-11 18:21:24 +00:00
|
|
|
/// Usage: `Winmdow::new(...).frame(|f| f.fill_color(Some(BLUE)))`
|
|
|
|
/// Not sure this is a good interface for this.
|
|
|
|
pub fn frame(mut self, frame: Frame) -> Self {
|
|
|
|
self.frame = Some(frame);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-04-25 20:49:57 +00:00
|
|
|
pub fn default_pos(mut self, default_pos: Pos2) -> Self {
|
2020-05-10 11:14:52 +00:00
|
|
|
self.area = self.area.default_pos(default_pos);
|
2020-04-25 20:49:57 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn default_size(mut self, default_size: Vec2) -> Self {
|
|
|
|
self.resize = self.resize.default_size(default_size);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-05-11 18:21:24 +00:00
|
|
|
pub fn default_rect(self, rect: Rect) -> Self {
|
|
|
|
self.default_pos(rect.min).default_size(rect.size())
|
|
|
|
}
|
|
|
|
|
2020-04-25 20:49:57 +00:00
|
|
|
pub fn min_size(mut self, min_size: Vec2) -> Self {
|
|
|
|
self.resize = self.resize.min_size(min_size);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn max_size(mut self, max_size: Vec2) -> Self {
|
|
|
|
self.resize = self.resize.max_size(max_size);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn fixed_size(mut self, size: Vec2) -> Self {
|
|
|
|
self.resize = self.resize.fixed_size(size);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Can you resize it with the mouse?
|
|
|
|
/// Note that a window can still auto-resize
|
|
|
|
pub fn resizable(mut self, resizable: bool) -> Self {
|
|
|
|
self.resize = self.resize.resizable(resizable);
|
|
|
|
self
|
|
|
|
}
|
2020-05-10 17:00:48 +00:00
|
|
|
|
|
|
|
/// Not resizable, just takes the size of its contents.
|
|
|
|
pub fn auto_sized(mut self) -> Self {
|
|
|
|
self.resize = self.resize.auto_sized();
|
|
|
|
self.scroll = None;
|
|
|
|
self
|
|
|
|
}
|
2020-05-11 18:21:24 +00:00
|
|
|
|
|
|
|
pub fn scroll(mut self, scroll: bool) -> Self {
|
|
|
|
if !scroll {
|
|
|
|
self.scroll = None;
|
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
2020-04-25 20:49:57 +00:00
|
|
|
}
|
|
|
|
|
2020-05-10 17:00:48 +00:00
|
|
|
impl<'open> Window<'open> {
|
2020-05-17 10:26:17 +00:00
|
|
|
pub fn show(
|
|
|
|
self,
|
|
|
|
ctx: &Arc<Context>,
|
|
|
|
add_contents: impl FnOnce(&mut Ui),
|
2020-05-20 16:58:21 +00:00
|
|
|
) -> Option<InteractInfo> {
|
|
|
|
self.show_impl(ctx, Box::new(add_contents))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn show_impl<'c>(
|
|
|
|
self,
|
|
|
|
ctx: &Arc<Context>,
|
|
|
|
add_contents: Box<dyn FnOnce(&mut Ui) + 'c>,
|
2020-05-17 10:26:17 +00:00
|
|
|
) -> Option<InteractInfo> {
|
2020-04-25 20:49:57 +00:00
|
|
|
let Window {
|
2020-05-08 15:16:32 +00:00
|
|
|
title_label,
|
2020-05-10 17:02:34 +00:00
|
|
|
open,
|
2020-05-10 11:14:52 +00:00
|
|
|
area,
|
2020-05-10 11:06:16 +00:00
|
|
|
frame,
|
2020-04-25 20:49:57 +00:00
|
|
|
resize,
|
2020-04-26 20:30:24 +00:00
|
|
|
scroll,
|
2020-04-25 20:49:57 +00:00
|
|
|
} = self;
|
2020-05-10 17:02:34 +00:00
|
|
|
|
2020-05-17 15:00:01 +00:00
|
|
|
if matches!(open, Some(false)) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2020-05-17 14:42:20 +00:00
|
|
|
let movable = area.is_movable();
|
|
|
|
let area = area.movable(false); // We move it manually
|
|
|
|
let resizable = resize.is_resizable();
|
|
|
|
let resize = resize.resizable(false); // We move it manually
|
|
|
|
|
|
|
|
let window_id = Id::new(title_label.text());
|
|
|
|
let area_layer = area.layer();
|
|
|
|
let resize_id = window_id.with("resize");
|
|
|
|
let collapsing_id = window_id.with("collapsing");
|
|
|
|
|
|
|
|
let resize = resize.id(resize_id);
|
|
|
|
|
2020-05-10 11:06:16 +00:00
|
|
|
let frame = frame.unwrap_or_else(|| Frame::window(&ctx.style()));
|
2020-05-08 15:16:32 +00:00
|
|
|
|
2020-05-17 14:42:20 +00:00
|
|
|
let full_interact = area.show(ctx, |ui| {
|
2020-05-17 10:26:17 +00:00
|
|
|
frame.show(ui, |ui| {
|
|
|
|
let default_expanded = true;
|
|
|
|
let mut collapsing = collapsing_header::State::from_memory_with_default_open(
|
|
|
|
ui,
|
|
|
|
collapsing_id,
|
|
|
|
default_expanded,
|
|
|
|
);
|
|
|
|
let show_close_button = open.is_some();
|
|
|
|
let title_bar = show_title_bar(
|
|
|
|
ui,
|
|
|
|
title_label,
|
|
|
|
show_close_button,
|
|
|
|
collapsing_id,
|
|
|
|
&mut collapsing,
|
|
|
|
);
|
|
|
|
|
2020-05-19 21:57:48 +00:00
|
|
|
let content = collapsing
|
|
|
|
.add_contents(ui, |ui| {
|
|
|
|
resize.show(ui, |ui| {
|
|
|
|
ui.add(Separator::new().line_width(1.0)); // TODO: nicer way to split window title from contents
|
|
|
|
if let Some(scroll) = scroll {
|
|
|
|
scroll.show(ui, add_contents)
|
|
|
|
} else {
|
|
|
|
add_contents(ui)
|
|
|
|
}
|
|
|
|
})
|
2020-05-17 07:44:09 +00:00
|
|
|
})
|
2020-05-19 21:57:48 +00:00
|
|
|
.map(|ri| ri.1);
|
2020-05-17 10:26:17 +00:00
|
|
|
|
2020-05-17 15:00:01 +00:00
|
|
|
ui.memory()
|
|
|
|
.collapsing_headers
|
|
|
|
.insert(collapsing_id, collapsing);
|
|
|
|
|
2020-05-17 10:26:17 +00:00
|
|
|
if let Some(open) = open {
|
|
|
|
// Add close button now that we know our full width:
|
|
|
|
|
|
|
|
let right = content
|
|
|
|
.map(|c| c.rect.right())
|
|
|
|
.unwrap_or(title_bar.rect.right());
|
|
|
|
|
|
|
|
let button_size = ui.style().start_icon_width;
|
|
|
|
let button_rect = Rect::from_min_size(
|
|
|
|
pos2(
|
|
|
|
right - ui.style().item_spacing.x - button_size,
|
|
|
|
title_bar.rect.center().y - 0.5 * button_size,
|
|
|
|
),
|
|
|
|
Vec2::splat(button_size),
|
|
|
|
);
|
|
|
|
|
|
|
|
if close_button(ui, button_rect).clicked {
|
|
|
|
*open = false;
|
|
|
|
}
|
|
|
|
}
|
2020-04-26 20:30:24 +00:00
|
|
|
})
|
2020-05-17 14:42:20 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
let resizable =
|
|
|
|
resizable && collapsing_header::State::is_open(ctx, collapsing_id).unwrap_or_default();
|
|
|
|
|
|
|
|
if movable || resizable {
|
|
|
|
let possible = PossibleInteractions { movable, resizable };
|
|
|
|
|
|
|
|
// TODO: not when collapsed, and not when resizing or moving is disabled etc
|
|
|
|
let pre_resize = ctx.round_rect_to_pixels(full_interact.rect);
|
|
|
|
let new_rect = resize_window(
|
|
|
|
ctx,
|
|
|
|
possible,
|
|
|
|
area_layer,
|
|
|
|
window_id.with("frame_resize"),
|
|
|
|
pre_resize,
|
|
|
|
);
|
2020-05-21 07:26:25 +00:00
|
|
|
if let Some(new_rect) = new_rect {
|
|
|
|
let new_rect = ctx.round_rect_to_pixels(new_rect);
|
2020-05-20 19:31:34 +00:00
|
|
|
// TODO: add this to a Window state instead as a command "move here next frame"
|
|
|
|
|
2020-05-17 14:42:20 +00:00
|
|
|
let mut area_state = ctx.memory().areas.get(area_layer.id).unwrap();
|
|
|
|
area_state.pos = new_rect.min;
|
|
|
|
ctx.memory().areas.set_state(area_layer, area_state);
|
|
|
|
|
|
|
|
let mut resize_state = ctx.memory().resize.get(&resize_id).cloned().unwrap();
|
|
|
|
// resize_state.size += new_rect.size() - pre_resize.size();
|
|
|
|
// resize_state.size = new_rect.size() - some margin;
|
|
|
|
resize_state.requested_size =
|
|
|
|
Some(resize_state.size + new_rect.size() - pre_resize.size());
|
|
|
|
ctx.memory().resize.insert(resize_id, resize_state);
|
|
|
|
|
|
|
|
ctx.memory().areas.move_to_top(area_layer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(full_interact)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
struct PossibleInteractions {
|
|
|
|
movable: bool,
|
|
|
|
resizable: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
2020-05-17 15:44:18 +00:00
|
|
|
pub(crate) struct WindowInteraction {
|
|
|
|
pub(crate) area_layer: Layer,
|
|
|
|
pub(crate) start_rect: Rect,
|
|
|
|
pub(crate) start_mouse_pos: Pos2,
|
|
|
|
pub(crate) left: bool,
|
|
|
|
pub(crate) right: bool,
|
|
|
|
pub(crate) top: bool,
|
|
|
|
pub(crate) bottom: bool,
|
2020-05-17 14:42:20 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 15:44:18 +00:00
|
|
|
impl WindowInteraction {
|
2020-05-17 14:42:20 +00:00
|
|
|
pub fn set_cursor(&self, ctx: &Context) {
|
|
|
|
if (self.left && self.top) || (self.right && self.bottom) {
|
|
|
|
ctx.output().cursor_icon = CursorIcon::ResizeNwSe;
|
|
|
|
} else if (self.right && self.top) || (self.left && self.bottom) {
|
|
|
|
ctx.output().cursor_icon = CursorIcon::ResizeNeSw;
|
|
|
|
} else if self.left || self.right {
|
|
|
|
ctx.output().cursor_icon = CursorIcon::ResizeHorizontal;
|
|
|
|
} else if self.bottom || self.top {
|
|
|
|
ctx.output().cursor_icon = CursorIcon::ResizeVertical;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_resize(&self) -> bool {
|
|
|
|
self.left || self.right || self.top || self.bottom
|
|
|
|
}
|
2020-05-17 07:44:09 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 14:42:20 +00:00
|
|
|
fn resize_window(
|
|
|
|
ctx: &Context,
|
|
|
|
possible: PossibleInteractions,
|
|
|
|
area_layer: Layer,
|
|
|
|
id: Id,
|
2020-05-21 07:26:25 +00:00
|
|
|
rect: Rect,
|
|
|
|
) -> Option<Rect> {
|
2020-05-17 15:44:18 +00:00
|
|
|
if let Some(window_interaction) = window_interaction(ctx, possible, area_layer, id, rect) {
|
|
|
|
window_interaction.set_cursor(ctx);
|
2020-05-21 08:20:16 +00:00
|
|
|
if let Some(mouse_pos) = ctx.input().mouse.pos {
|
2020-05-21 07:26:25 +00:00
|
|
|
let mut rect = window_interaction.start_rect; // prevent drift
|
2020-05-17 14:42:20 +00:00
|
|
|
|
2020-05-17 15:44:18 +00:00
|
|
|
if window_interaction.is_resize() {
|
|
|
|
if window_interaction.left {
|
2020-05-17 14:42:20 +00:00
|
|
|
rect.min.x = ctx.round_to_pixel(mouse_pos.x);
|
2020-05-17 15:44:18 +00:00
|
|
|
} else if window_interaction.right {
|
2020-05-17 14:42:20 +00:00
|
|
|
rect.max.x = ctx.round_to_pixel(mouse_pos.x);
|
|
|
|
}
|
|
|
|
|
2020-05-17 15:44:18 +00:00
|
|
|
if window_interaction.top {
|
2020-05-17 14:42:20 +00:00
|
|
|
rect.min.y = ctx.round_to_pixel(mouse_pos.y);
|
2020-05-17 15:44:18 +00:00
|
|
|
} else if window_interaction.bottom {
|
2020-05-17 14:42:20 +00:00
|
|
|
rect.max.y = ctx.round_to_pixel(mouse_pos.y);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// movevement
|
2020-05-17 15:44:18 +00:00
|
|
|
rect = rect.translate(mouse_pos - window_interaction.start_mouse_pos);
|
2020-05-17 14:42:20 +00:00
|
|
|
}
|
2020-05-21 07:26:25 +00:00
|
|
|
|
|
|
|
return Some(rect);
|
2020-05-17 14:42:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-21 07:26:25 +00:00
|
|
|
None
|
2020-05-17 14:42:20 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 15:44:18 +00:00
|
|
|
fn window_interaction(
|
2020-05-17 14:42:20 +00:00
|
|
|
ctx: &Context,
|
|
|
|
possible: PossibleInteractions,
|
|
|
|
area_layer: Layer,
|
|
|
|
id: Id,
|
|
|
|
rect: Rect,
|
2020-05-17 15:44:18 +00:00
|
|
|
) -> Option<WindowInteraction> {
|
2020-05-17 14:42:20 +00:00
|
|
|
{
|
|
|
|
let active_id = ctx.memory().active_id;
|
|
|
|
|
|
|
|
if active_id.is_some() && active_id != Some(id) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-17 15:44:18 +00:00
|
|
|
let mut window_interaction = { ctx.memory().window_interaction.clone() };
|
2020-05-17 14:42:20 +00:00
|
|
|
|
2020-05-17 15:44:18 +00:00
|
|
|
if window_interaction.is_none() {
|
|
|
|
if let Some(hover_window_interaction) = resize_hover(ctx, possible, area_layer, rect) {
|
|
|
|
hover_window_interaction.set_cursor(ctx);
|
2020-05-21 08:20:16 +00:00
|
|
|
if ctx.input().mouse.pressed {
|
2020-05-17 14:42:20 +00:00
|
|
|
ctx.memory().active_id = Some(id);
|
2020-05-17 15:44:18 +00:00
|
|
|
window_interaction = Some(hover_window_interaction);
|
|
|
|
ctx.memory().window_interaction = window_interaction;
|
2020-05-17 14:42:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-17 15:44:18 +00:00
|
|
|
if let Some(window_interaction) = window_interaction {
|
2020-05-17 14:42:20 +00:00
|
|
|
let is_active = ctx.memory().active_id == Some(id);
|
|
|
|
|
2020-05-17 15:44:18 +00:00
|
|
|
if is_active && window_interaction.area_layer == area_layer {
|
|
|
|
return Some(window_interaction);
|
2020-05-17 14:42:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn resize_hover(
|
|
|
|
ctx: &Context,
|
|
|
|
possible: PossibleInteractions,
|
|
|
|
area_layer: Layer,
|
|
|
|
rect: Rect,
|
2020-05-17 15:44:18 +00:00
|
|
|
) -> Option<WindowInteraction> {
|
2020-05-21 08:20:16 +00:00
|
|
|
if let Some(mouse_pos) = ctx.input().mouse.pos {
|
2020-05-17 14:42:20 +00:00
|
|
|
if let Some(top_layer) = ctx.memory().layer_at(mouse_pos) {
|
|
|
|
if top_layer != area_layer && top_layer.order != Order::Background {
|
|
|
|
return None; // Another window is on top here
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let side_interact_radius = 5.0; // TODO: from style
|
|
|
|
let corner_interact_radius = 10.0; // TODO
|
|
|
|
if rect.expand(side_interact_radius).contains(mouse_pos) {
|
|
|
|
let (mut left, mut right, mut top, mut bottom) = Default::default();
|
|
|
|
if possible.resizable {
|
|
|
|
right = (rect.right() - mouse_pos.x).abs() <= side_interact_radius;
|
|
|
|
bottom = (rect.bottom() - mouse_pos.y).abs() <= side_interact_radius;
|
|
|
|
|
|
|
|
if rect.right_bottom().dist(mouse_pos) < corner_interact_radius {
|
|
|
|
right = true;
|
|
|
|
bottom = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if possible.movable {
|
|
|
|
left = (rect.left() - mouse_pos.x).abs() <= side_interact_radius;
|
|
|
|
top = (rect.top() - mouse_pos.y).abs() <= side_interact_radius;
|
|
|
|
|
|
|
|
if rect.right_top().dist(mouse_pos) < corner_interact_radius {
|
|
|
|
right = true;
|
|
|
|
top = true;
|
|
|
|
}
|
|
|
|
if rect.left_top().dist(mouse_pos) < corner_interact_radius {
|
|
|
|
left = true;
|
|
|
|
top = true;
|
|
|
|
}
|
|
|
|
if rect.left_bottom().dist(mouse_pos) < corner_interact_radius {
|
|
|
|
left = true;
|
|
|
|
bottom = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let any_resize = left || right || top || bottom;
|
|
|
|
|
|
|
|
if !any_resize && !possible.movable {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
if any_resize || possible.movable {
|
2020-05-17 15:44:18 +00:00
|
|
|
Some(WindowInteraction {
|
2020-05-17 14:42:20 +00:00
|
|
|
area_layer,
|
|
|
|
start_rect: rect,
|
|
|
|
start_mouse_pos: mouse_pos,
|
|
|
|
left,
|
|
|
|
right,
|
|
|
|
top,
|
|
|
|
bottom,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2020-05-17 10:26:17 +00:00
|
|
|
fn show_title_bar(
|
|
|
|
ui: &mut Ui,
|
|
|
|
title_label: Label,
|
|
|
|
show_close_button: bool,
|
|
|
|
collapsing_id: Id,
|
|
|
|
collapsing: &mut collapsing_header::State,
|
|
|
|
) -> InteractInfo {
|
|
|
|
ui.inner_layout(Layout::horizontal(Align::Center), |ui| {
|
|
|
|
ui.set_desired_height(title_label.font_height(ui));
|
|
|
|
|
|
|
|
let item_spacing = ui.style().item_spacing;
|
|
|
|
let button_size = ui.style().start_icon_width;
|
|
|
|
|
|
|
|
{
|
|
|
|
// TODO: make clickable radius larger
|
|
|
|
ui.reserve_space(vec2(0.0, 0.0), None); // HACK: will add left spacing
|
|
|
|
|
|
|
|
let collapse_button_interact =
|
|
|
|
ui.reserve_space(Vec2::splat(button_size), Some(collapsing_id));
|
|
|
|
if collapse_button_interact.clicked {
|
|
|
|
// TODO: also do this when double-clicking window title
|
|
|
|
collapsing.toggle(ui);
|
|
|
|
}
|
|
|
|
collapsing.paint_icon(ui, &collapse_button_interact);
|
2020-05-17 07:44:09 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 10:26:17 +00:00
|
|
|
let title_rect = ui.add(title_label).rect;
|
|
|
|
|
|
|
|
if show_close_button {
|
|
|
|
// Reserve space for close button which will be added later:
|
|
|
|
let close_max_x = title_rect.right() + item_spacing.x + button_size + item_spacing.x;
|
|
|
|
let close_max_x = close_max_x.max(ui.rect_finite().right());
|
|
|
|
let close_rect = Rect::from_min_size(
|
|
|
|
pos2(
|
|
|
|
close_max_x - button_size,
|
|
|
|
title_rect.center().y - 0.5 * button_size,
|
|
|
|
),
|
|
|
|
Vec2::splat(button_size),
|
|
|
|
);
|
|
|
|
ui.expand_to_include_child(close_rect);
|
|
|
|
}
|
|
|
|
})
|
2020-05-19 21:57:48 +00:00
|
|
|
.1
|
2020-05-17 07:44:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn close_button(ui: &mut Ui, rect: Rect) -> InteractInfo {
|
|
|
|
let close_id = ui.make_child_id("window_close_button");
|
|
|
|
let interact = ui.interact_rect(rect, close_id);
|
|
|
|
ui.expand_to_include_child(interact.rect);
|
|
|
|
|
|
|
|
let stroke_color = ui.style().interact(&interact).stroke_color;
|
|
|
|
let stroke_width = ui.style().interact(&interact).stroke_width;
|
|
|
|
ui.add_paint_cmd(PaintCmd::line_segment(
|
|
|
|
[rect.left_top(), rect.right_bottom()],
|
|
|
|
stroke_color,
|
|
|
|
stroke_width,
|
|
|
|
));
|
|
|
|
ui.add_paint_cmd(PaintCmd::line_segment(
|
|
|
|
[rect.right_top(), rect.left_bottom()],
|
|
|
|
stroke_color,
|
|
|
|
stroke_width,
|
|
|
|
));
|
|
|
|
interact
|
2020-04-25 20:49:57 +00:00
|
|
|
}
|