[window] double-click title bar to collapse winodw
This commit is contained in:
parent
6ab7cffd7f
commit
19cbe6bd6e
5 changed files with 119 additions and 65 deletions
|
@ -168,51 +168,54 @@ impl<'open> Window<'open> {
|
||||||
|
|
||||||
let mut area = area.begin(ctx);
|
let mut area = area.begin(ctx);
|
||||||
{
|
{
|
||||||
|
// BEGIN FRAME --------------------------------
|
||||||
let mut frame = frame.begin(&mut area.content_ui);
|
let mut frame = frame.begin(&mut area.content_ui);
|
||||||
|
|
||||||
let content_rect;
|
let default_expanded = true;
|
||||||
let title_bar;
|
let mut collapsing = collapsing_header::State::from_memory_with_default_open(
|
||||||
{
|
&mut frame.content_ui,
|
||||||
let ui = &mut frame.content_ui;
|
collapsing_id,
|
||||||
|
default_expanded,
|
||||||
|
);
|
||||||
|
let show_close_button = open.is_some();
|
||||||
|
let title_bar = show_title_bar(
|
||||||
|
&mut frame.content_ui,
|
||||||
|
title_label,
|
||||||
|
show_close_button,
|
||||||
|
collapsing_id,
|
||||||
|
&mut collapsing,
|
||||||
|
);
|
||||||
|
|
||||||
let default_expanded = true;
|
let content_rect = collapsing
|
||||||
let mut collapsing = collapsing_header::State::from_memory_with_default_open(
|
.add_contents(&mut frame.content_ui, |ui| {
|
||||||
ui,
|
resize.show(ui, |ui| {
|
||||||
collapsing_id,
|
// Add some spacing (item_spacing) between title and content:
|
||||||
default_expanded,
|
ui.allocate_space(Vec2::zero());
|
||||||
);
|
|
||||||
let show_close_button = open.is_some();
|
|
||||||
title_bar = show_title_bar(
|
|
||||||
ui,
|
|
||||||
title_label,
|
|
||||||
show_close_button,
|
|
||||||
collapsing_id,
|
|
||||||
&mut collapsing,
|
|
||||||
);
|
|
||||||
|
|
||||||
content_rect = collapsing
|
if let Some(scroll) = scroll {
|
||||||
.add_contents(ui, |ui| {
|
scroll.show(ui, add_contents)
|
||||||
resize.show(ui, |ui| {
|
} else {
|
||||||
// Add some spacing (item_spacing) between title and content:
|
add_contents(ui)
|
||||||
ui.allocate_space(Vec2::zero());
|
}
|
||||||
|
|
||||||
if let Some(scroll) = scroll {
|
|
||||||
scroll.show(ui, add_contents)
|
|
||||||
} else {
|
|
||||||
add_contents(ui)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.map(|ri| ri.1);
|
})
|
||||||
|
.map(|ri| ri.1);
|
||||||
ui.memory()
|
|
||||||
.collapsing_headers
|
|
||||||
.insert(collapsing_id, collapsing);
|
|
||||||
}
|
|
||||||
|
|
||||||
let outer_rect = frame.end(&mut area.content_ui);
|
let outer_rect = frame.end(&mut area.content_ui);
|
||||||
|
// END FRAME --------------------------------
|
||||||
|
|
||||||
title_bar.ui(&mut area.content_ui, outer_rect, content_rect, open);
|
title_bar.ui(
|
||||||
|
&mut area.content_ui,
|
||||||
|
outer_rect,
|
||||||
|
content_rect,
|
||||||
|
open,
|
||||||
|
&mut collapsing,
|
||||||
|
);
|
||||||
|
|
||||||
|
area.content_ui
|
||||||
|
.memory()
|
||||||
|
.collapsing_headers
|
||||||
|
.insert(collapsing_id, collapsing);
|
||||||
|
|
||||||
let interaction = if possible.movable || possible.resizable {
|
let interaction = if possible.movable || possible.resizable {
|
||||||
interact(
|
interact(
|
||||||
|
@ -524,7 +527,7 @@ fn show_title_bar(
|
||||||
collapsing_id: Id,
|
collapsing_id: Id,
|
||||||
collapsing: &mut collapsing_header::State,
|
collapsing: &mut collapsing_header::State,
|
||||||
) -> TitleBar {
|
) -> TitleBar {
|
||||||
let tb_interact = ui.inner_layout(Layout::horizontal(Align::Center), |ui| {
|
let title_bar_and_rect = ui.inner_layout(Layout::horizontal(Align::Center), |ui| {
|
||||||
ui.set_desired_height(title_label.font_height(ui));
|
ui.set_desired_height(title_label.font_height(ui));
|
||||||
|
|
||||||
let item_spacing = ui.style().item_spacing;
|
let item_spacing = ui.style().item_spacing;
|
||||||
|
@ -537,7 +540,6 @@ fn show_title_bar(
|
||||||
let rect = ui.allocate_space(Vec2::splat(button_size));
|
let rect = ui.allocate_space(Vec2::splat(button_size));
|
||||||
let collapse_button_interact = ui.interact(rect, collapsing_id, Sense::click());
|
let collapse_button_interact = ui.interact(rect, collapsing_id, Sense::click());
|
||||||
if collapse_button_interact.clicked {
|
if collapse_button_interact.clicked {
|
||||||
// TODO: also do this when double-clicking window title
|
|
||||||
collapsing.toggle(ui);
|
collapsing.toggle(ui);
|
||||||
}
|
}
|
||||||
collapsing.paint_icon(ui, &collapse_button_interact);
|
collapsing.paint_icon(ui, &collapse_button_interact);
|
||||||
|
@ -569,28 +571,35 @@ fn show_title_bar(
|
||||||
});
|
});
|
||||||
|
|
||||||
TitleBar {
|
TitleBar {
|
||||||
rect: tb_interact.1,
|
rect: title_bar_and_rect.1,
|
||||||
..tb_interact.0
|
..title_bar_and_rect.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TitleBar {
|
impl TitleBar {
|
||||||
fn ui(
|
fn ui(
|
||||||
self,
|
mut self,
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
outer_rect: Rect,
|
outer_rect: Rect,
|
||||||
content_rect: Option<Rect>,
|
content_rect: Option<Rect>,
|
||||||
open: Option<&mut bool>,
|
open: Option<&mut bool>,
|
||||||
|
collapsing: &mut collapsing_header::State,
|
||||||
) {
|
) {
|
||||||
|
if let Some(content_rect) = content_rect {
|
||||||
|
// Now we know how large we got to be:
|
||||||
|
self.rect.max.x = content_rect.max.x;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(open) = open {
|
if let Some(open) = open {
|
||||||
// Add close button now that we know our full width:
|
// Add close button now that we know our full width:
|
||||||
if self.close_button_ui(ui, &content_rect).clicked {
|
if self.close_button_ui(ui).clicked {
|
||||||
*open = false;
|
*open = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: pick style for title based on move interaction
|
// TODO: pick style for title based on move interaction
|
||||||
self.title_ui(ui);
|
self.title_label
|
||||||
|
.paint_galley(ui, self.title_rect.min, self.title_galley);
|
||||||
|
|
||||||
if let Some(content_rect) = content_rect {
|
if let Some(content_rect) = content_rect {
|
||||||
// paint separator between title and content:
|
// paint separator between title and content:
|
||||||
|
@ -602,20 +611,21 @@ impl TitleBar {
|
||||||
style: ui.style().interact.inactive.rect_outline.unwrap(),
|
style: ui.style().interact.inactive.rect_outline.unwrap(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let title_bar_id = ui.make_child_id("title_bar");
|
||||||
|
if ui
|
||||||
|
.interact(self.rect, title_bar_id, Sense::click())
|
||||||
|
.double_clicked
|
||||||
|
{
|
||||||
|
collapsing.toggle(ui);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title_ui(self, ui: &mut Ui) {
|
fn close_button_ui(&self, ui: &mut Ui) -> InteractInfo {
|
||||||
self.title_label
|
|
||||||
.paint_galley(ui, self.title_rect.min, self.title_galley);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn close_button_ui(&self, ui: &mut Ui, content_rect: &Option<Rect>) -> InteractInfo {
|
|
||||||
let right = content_rect.map(|c| c.right()).unwrap_or(self.rect.right());
|
|
||||||
|
|
||||||
let button_size = ui.style().start_icon_width;
|
let button_size = ui.style().start_icon_width;
|
||||||
let button_rect = Rect::from_min_size(
|
let button_rect = Rect::from_min_size(
|
||||||
pos2(
|
pos2(
|
||||||
right - ui.style().item_spacing.x - button_size,
|
self.rect.right() - ui.style().item_spacing.x - button_size,
|
||||||
self.rect.center().y - 0.5 * button_size,
|
self.rect.center().y - 0.5 * button_size,
|
||||||
),
|
),
|
||||||
Vec2::splat(button_size),
|
Vec2::splat(button_size),
|
||||||
|
|
|
@ -36,7 +36,6 @@ pub struct Context {
|
||||||
paint_stats: Mutex<PaintStats>,
|
paint_stats: Mutex<PaintStats>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove this impl.
|
|
||||||
impl Clone for Context {
|
impl Clone for Context {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Context {
|
Context {
|
||||||
|
@ -280,6 +279,7 @@ impl Context {
|
||||||
rect,
|
rect,
|
||||||
hovered,
|
hovered,
|
||||||
clicked: false,
|
clicked: false,
|
||||||
|
double_clicked: false,
|
||||||
active: false,
|
active: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -299,6 +299,7 @@ impl Context {
|
||||||
rect,
|
rect,
|
||||||
hovered: true,
|
hovered: true,
|
||||||
clicked: false,
|
clicked: false,
|
||||||
|
double_clicked: false,
|
||||||
active: false,
|
active: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -321,14 +322,17 @@ impl Context {
|
||||||
rect,
|
rect,
|
||||||
hovered,
|
hovered,
|
||||||
clicked: false,
|
clicked: false,
|
||||||
|
double_clicked: false,
|
||||||
active: false,
|
active: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if self.input.mouse.released {
|
} else if self.input.mouse.released {
|
||||||
|
let clicked = hovered && active;
|
||||||
InteractInfo {
|
InteractInfo {
|
||||||
rect,
|
rect,
|
||||||
hovered,
|
hovered,
|
||||||
clicked: hovered && active,
|
clicked,
|
||||||
|
double_clicked: clicked && self.input.mouse.double_click,
|
||||||
active,
|
active,
|
||||||
}
|
}
|
||||||
} else if self.input.mouse.down {
|
} else if self.input.mouse.down {
|
||||||
|
@ -336,6 +340,7 @@ impl Context {
|
||||||
rect,
|
rect,
|
||||||
hovered: hovered && active,
|
hovered: hovered && active,
|
||||||
clicked: false,
|
clicked: false,
|
||||||
|
double_clicked: false,
|
||||||
active,
|
active,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -343,6 +348,7 @@ impl Context {
|
||||||
rect,
|
rect,
|
||||||
hovered,
|
hovered,
|
||||||
clicked: false,
|
clicked: false,
|
||||||
|
double_clicked: false,
|
||||||
active,
|
active,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,11 @@ use serde_derive::Deserialize;
|
||||||
|
|
||||||
use crate::{math::*, movement_tracker::MovementTracker};
|
use crate::{math::*, movement_tracker::MovementTracker};
|
||||||
|
|
||||||
|
/// If mouse moves more than this, it is no longer a click (but maybe a drag)
|
||||||
|
const MAX_CLICK_DIST: f32 = 6.0;
|
||||||
|
/// The new mouse press must come within this many seconds from previous mouse release
|
||||||
|
const MAX_CLICK_DELAY: f64 = 0.3;
|
||||||
|
|
||||||
/// What the integration gives to the gui.
|
/// What the integration gives to the gui.
|
||||||
/// All coordinates in emigui is in point/logical coordinates.
|
/// All coordinates in emigui is in point/logical coordinates.
|
||||||
#[derive(Clone, Debug, Default, Deserialize)]
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
|
@ -77,6 +82,21 @@ pub struct MouseInput {
|
||||||
/// The mouse went from down to !down
|
/// The mouse went from down to !down
|
||||||
pub released: bool,
|
pub released: bool,
|
||||||
|
|
||||||
|
/// If the mouse is down, will it register as a click when released?
|
||||||
|
/// Set to true on mouse down, set to false when mouse moves too much.
|
||||||
|
pub could_be_click: bool,
|
||||||
|
|
||||||
|
/// Was there a click?
|
||||||
|
/// Did a mouse button get released this frame closely after going down?
|
||||||
|
pub click: bool,
|
||||||
|
|
||||||
|
/// Was there a double-click?
|
||||||
|
pub double_click: bool,
|
||||||
|
|
||||||
|
/// When did the mouse get click last?
|
||||||
|
/// Used to check for double-clicks.
|
||||||
|
pub last_click_time: f64,
|
||||||
|
|
||||||
/// Current position of the mouse in points.
|
/// Current position of the mouse in points.
|
||||||
/// None for touch screens when finger is not down.
|
/// None for touch screens when finger is not down.
|
||||||
pub pos: Option<Pos2>,
|
pub pos: Option<Pos2>,
|
||||||
|
@ -84,10 +104,6 @@ pub struct MouseInput {
|
||||||
/// Where did the current click/drag originate?
|
/// Where did the current click/drag originate?
|
||||||
pub press_origin: Option<Pos2>,
|
pub press_origin: Option<Pos2>,
|
||||||
|
|
||||||
/// If the mouse is down, will it register as a click when released?
|
|
||||||
/// Set to true on mouse down, set to false when mouse moves too much.
|
|
||||||
pub could_be_click: bool,
|
|
||||||
|
|
||||||
/// How much the mouse moved compared to last frame, in points.
|
/// How much the mouse moved compared to last frame, in points.
|
||||||
pub delta: Vec2,
|
pub delta: Vec2,
|
||||||
|
|
||||||
|
@ -105,9 +121,12 @@ impl Default for MouseInput {
|
||||||
down: false,
|
down: false,
|
||||||
pressed: false,
|
pressed: false,
|
||||||
released: false,
|
released: false,
|
||||||
|
could_be_click: false,
|
||||||
|
click: false,
|
||||||
|
double_click: false,
|
||||||
|
last_click_time: std::f64::NEG_INFINITY,
|
||||||
pos: None,
|
pos: None,
|
||||||
press_origin: None,
|
press_origin: None,
|
||||||
could_be_click: false,
|
|
||||||
delta: Vec2::zero(),
|
delta: Vec2::zero(),
|
||||||
velocity: Vec2::zero(),
|
velocity: Vec2::zero(),
|
||||||
pos_tracker: MovementTracker::new(1000, 0.1),
|
pos_tracker: MovementTracker::new(1000, 0.1),
|
||||||
|
@ -181,8 +200,15 @@ impl MouseInput {
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let pressed = !self.down && new.mouse_down;
|
let pressed = !self.down && new.mouse_down;
|
||||||
|
|
||||||
|
let released = self.down && !new.mouse_down;
|
||||||
|
let click = released && self.could_be_click;
|
||||||
|
let double_click = click && (new.time - self.last_click_time) < MAX_CLICK_DELAY;
|
||||||
let mut press_origin = self.press_origin;
|
let mut press_origin = self.press_origin;
|
||||||
let mut could_be_click = self.could_be_click;
|
let mut could_be_click = self.could_be_click;
|
||||||
|
let mut last_click_time = self.last_click_time;
|
||||||
|
if click {
|
||||||
|
last_click_time = new.time
|
||||||
|
}
|
||||||
|
|
||||||
if pressed {
|
if pressed {
|
||||||
press_origin = new.mouse_pos;
|
press_origin = new.mouse_pos;
|
||||||
|
@ -192,8 +218,6 @@ impl MouseInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Some(press_origin), Some(mouse_pos)) = (new.mouse_pos, press_origin) {
|
if let (Some(press_origin), Some(mouse_pos)) = (new.mouse_pos, press_origin) {
|
||||||
// If mouse moves more than this, it is no longer a click (but maybe a drag)
|
|
||||||
const MAX_CLICK_DIST: f32 = 6.0;
|
|
||||||
could_be_click &= press_origin.distance(mouse_pos) < MAX_CLICK_DIST;
|
could_be_click &= press_origin.distance(mouse_pos) < MAX_CLICK_DIST;
|
||||||
} else {
|
} else {
|
||||||
could_be_click = false;
|
could_be_click = false;
|
||||||
|
@ -212,10 +236,13 @@ impl MouseInput {
|
||||||
MouseInput {
|
MouseInput {
|
||||||
down: new.mouse_down && new.mouse_pos.is_some(),
|
down: new.mouse_down && new.mouse_pos.is_some(),
|
||||||
pressed,
|
pressed,
|
||||||
released: self.down && !new.mouse_down,
|
released,
|
||||||
|
could_be_click,
|
||||||
|
click,
|
||||||
|
double_click,
|
||||||
|
last_click_time,
|
||||||
pos: new.mouse_pos,
|
pos: new.mouse_pos,
|
||||||
press_origin,
|
press_origin,
|
||||||
could_be_click,
|
|
||||||
delta,
|
delta,
|
||||||
velocity,
|
velocity,
|
||||||
pos_tracker: self.pos_tracker,
|
pos_tracker: self.pos_tracker,
|
||||||
|
@ -281,9 +308,12 @@ impl MouseInput {
|
||||||
ui.add(label!("down: {}", self.down));
|
ui.add(label!("down: {}", self.down));
|
||||||
ui.add(label!("pressed: {}", self.pressed));
|
ui.add(label!("pressed: {}", self.pressed));
|
||||||
ui.add(label!("released: {}", self.released));
|
ui.add(label!("released: {}", self.released));
|
||||||
|
ui.add(label!("could_be_click: {}", self.could_be_click));
|
||||||
|
ui.add(label!("click: {}", self.click));
|
||||||
|
ui.add(label!("double_click: {}", self.double_click));
|
||||||
|
ui.add(label!("last_click_time: {:.3}", self.last_click_time));
|
||||||
ui.add(label!("pos: {:?}", self.pos));
|
ui.add(label!("pos: {:?}", self.pos));
|
||||||
ui.add(label!("press_origin: {:?}", self.press_origin));
|
ui.add(label!("press_origin: {:?}", self.press_origin));
|
||||||
ui.add(label!("could_be_click: {}", self.could_be_click));
|
|
||||||
ui.add(label!("delta: {:?}", self.delta));
|
ui.add(label!("delta: {:?}", self.delta));
|
||||||
ui.add(label!(
|
ui.add(label!(
|
||||||
"velocity: [{:3.0} {:3.0}] points/sec",
|
"velocity: [{:3.0} {:3.0}] points/sec",
|
||||||
|
|
|
@ -46,6 +46,8 @@ pub struct InteractInfo {
|
||||||
/// The mouse pressed this thing ealier, and now released on this thing too.
|
/// The mouse pressed this thing ealier, and now released on this thing too.
|
||||||
pub clicked: bool,
|
pub clicked: bool,
|
||||||
|
|
||||||
|
pub double_clicked: bool,
|
||||||
|
|
||||||
/// The mouse is interacting with this thing (e.g. dragging it or holding it)
|
/// The mouse is interacting with this thing (e.g. dragging it or holding it)
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
|
|
||||||
|
@ -58,6 +60,7 @@ impl InteractInfo {
|
||||||
Self {
|
Self {
|
||||||
hovered: false,
|
hovered: false,
|
||||||
clicked: false,
|
clicked: false,
|
||||||
|
double_clicked: false,
|
||||||
active: false,
|
active: false,
|
||||||
rect: Rect::nothing(),
|
rect: Rect::nothing(),
|
||||||
}
|
}
|
||||||
|
@ -67,6 +70,7 @@ impl InteractInfo {
|
||||||
Self {
|
Self {
|
||||||
hovered: self.hovered || other.hovered,
|
hovered: self.hovered || other.hovered,
|
||||||
clicked: self.clicked || other.clicked,
|
clicked: self.clicked || other.clicked,
|
||||||
|
double_clicked: self.double_clicked || other.double_clicked,
|
||||||
active: self.active || other.active,
|
active: self.active || other.active,
|
||||||
rect: self.rect.union(other.rect),
|
rect: self.rect.union(other.rect),
|
||||||
}
|
}
|
||||||
|
@ -83,6 +87,8 @@ pub struct GuiResponse {
|
||||||
/// The mouse clicked this thing this frame
|
/// The mouse clicked this thing this frame
|
||||||
pub clicked: bool,
|
pub clicked: bool,
|
||||||
|
|
||||||
|
pub double_clicked: bool,
|
||||||
|
|
||||||
/// The mouse is interacting with this thing (e.g. dragging it)
|
/// The mouse is interacting with this thing (e.g. dragging it)
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
|
|
||||||
|
@ -115,6 +121,7 @@ impl Into<InteractInfo> for GuiResponse {
|
||||||
InteractInfo {
|
InteractInfo {
|
||||||
hovered: self.hovered,
|
hovered: self.hovered,
|
||||||
clicked: self.clicked,
|
clicked: self.clicked,
|
||||||
|
double_clicked: self.double_clicked,
|
||||||
active: self.active,
|
active: self.active,
|
||||||
rect: self.rect,
|
rect: self.rect,
|
||||||
}
|
}
|
||||||
|
|
|
@ -323,6 +323,7 @@ impl Ui {
|
||||||
GuiResponse {
|
GuiResponse {
|
||||||
hovered: interact.hovered,
|
hovered: interact.hovered,
|
||||||
clicked: interact.clicked,
|
clicked: interact.clicked,
|
||||||
|
double_clicked: interact.double_clicked,
|
||||||
active: interact.active,
|
active: interact.active,
|
||||||
rect: interact.rect,
|
rect: interact.rect,
|
||||||
ctx: self.ctx.clone(),
|
ctx: self.ctx.clone(),
|
||||||
|
|
Loading…
Reference in a new issue