-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Programmatically open / closing windows #3517
base: master
Are you sure you want to change the base?
Changes from 5 commits
b4ce7df
d0a4fa2
6ea82f4
d923898
2e901b8
3731ae5
c8ef4aa
dc20fc2
0d74098
f4de1d8
85b6b1a
256aa4d
b7fa7c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,14 +27,38 @@ use super::*; | |
#[must_use = "You should call .show()"] | ||
pub struct Window<'open> { | ||
title: WidgetText, | ||
open: Option<&'open mut bool>, | ||
area: Area, | ||
frame: Option<Frame>, | ||
resize: Resize, | ||
scroll: ScrollArea, | ||
collapsible: bool, | ||
default_open: bool, | ||
with_title_bar: bool, | ||
with_closing_btn: bool, | ||
display_event: Option<DisplayEvent>, | ||
open: Option<&'open mut bool>, | ||
} | ||
|
||
#[derive(PartialEq, Default, Clone, Copy, Debug)] | ||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] | ||
pub enum DisplayEvent { | ||
#[default] | ||
Expand, | ||
Collapse, | ||
Hide, | ||
ToggleCollapse, | ||
ToggleHidden, | ||
Comment on lines
+49
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These need docstrings. What does it mean to hide a window? Does it mean "Close"? If so, please name it that because "Close" is the antonym of "Open". |
||
} | ||
|
||
// Helper function for user space | ||
pub trait SetEvent { | ||
Comment on lines
+56
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What would this be used for? |
||
fn set(&mut self, display_event: DisplayEvent); | ||
} | ||
|
||
impl SetEvent for Option<DisplayEvent> { | ||
fn set(&mut self, display_event: DisplayEvent) { | ||
let _ = self.insert(display_event); | ||
} | ||
} | ||
|
||
impl<'open> Window<'open> { | ||
|
@@ -44,6 +68,7 @@ impl<'open> Window<'open> { | |
pub fn new(title: impl Into<WidgetText>) -> Self { | ||
let title = title.into().fallback_text_style(TextStyle::Heading); | ||
let area = Area::new(Id::new(title.text())).constrain(true); | ||
|
||
Self { | ||
title, | ||
open: None, | ||
|
@@ -55,8 +80,10 @@ impl<'open> Window<'open> { | |
.default_size([340.0, 420.0]), // Default inner size of a window | ||
scroll: ScrollArea::neither(), | ||
collapsible: true, | ||
default_open: true, | ||
with_title_bar: true, | ||
with_closing_btn: false, | ||
default_open: true, | ||
display_event: None, | ||
} | ||
} | ||
|
||
|
@@ -71,8 +98,15 @@ impl<'open> Window<'open> { | |
/// * If `*open == false`, the window will not be visible. | ||
/// * If `*open == true`, the window will have a close button. | ||
/// * If the close button is pressed, `*open` will be set to `false`. | ||
//#[deprecated = "Use display_state and closing_button instead"] | ||
pub fn open(mut self, open: &'open mut bool) -> Self { | ||
self.open = Some(open); | ||
self.with_closing_btn = true; | ||
self | ||
} | ||
|
||
pub fn closing_button(mut self, closing_button: bool) -> Self { | ||
self.with_closing_btn = closing_button; | ||
self | ||
} | ||
|
||
|
@@ -205,6 +239,11 @@ impl<'open> Window<'open> { | |
self | ||
} | ||
|
||
pub fn display_event(mut self, new_event: &mut Option<DisplayEvent>) -> Self { | ||
self.display_event = new_event.take(); | ||
self | ||
} | ||
|
||
/// Set initial size of the window. | ||
pub fn default_size(mut self, default_size: impl Into<Vec2>) -> Self { | ||
self.resize = self.resize.default_size(default_size); | ||
|
@@ -317,33 +356,58 @@ impl<'open> Window<'open> { | |
) -> Option<InnerResponse<Option<R>>> { | ||
let Window { | ||
title, | ||
open, | ||
mut open, | ||
area, | ||
frame, | ||
resize, | ||
scroll, | ||
collapsible, | ||
default_open, | ||
with_title_bar, | ||
with_closing_btn, | ||
default_open, | ||
display_event, | ||
} = self; | ||
|
||
let frame = frame.unwrap_or_else(|| Frame::window(&ctx.style())); | ||
|
||
let is_explicitly_closed = matches!(open, Some(false)); | ||
let is_open = !is_explicitly_closed || ctx.memory(|mem| mem.everything_is_visible()); | ||
let area_id = area.id; | ||
let mut collapsing = CollapsingState::load(ctx, area_id.with("collapsing"), default_open); | ||
|
||
// Borrow open to not consume it. | ||
// This is for backwards compatibility with events | ||
fn backwards_compatibility_open( | ||
open: &mut Option<&mut bool>, | ||
collapsing: &CollapsingState, | ||
) { | ||
if let Some(open) = open { | ||
**open = !collapsing.is_hidden(); | ||
} | ||
} | ||
|
||
match display_event { | ||
Some(DisplayEvent::Hide) => collapsing.set_hidden(true), | ||
Some(DisplayEvent::Expand) => collapsing.set_open(true), | ||
Some(DisplayEvent::Collapse) => collapsing.set_open(false), | ||
Some(DisplayEvent::ToggleHidden) => collapsing.toggle_hidden(), | ||
Some(DisplayEvent::ToggleCollapse) => collapsing.set_open(!collapsing.is_open()), | ||
None => collapsing.set_hidden(matches!(open, Some(false))), | ||
} | ||
|
||
let is_open = !collapsing.is_hidden() || ctx.memory(|mem| mem.everything_is_visible()); | ||
area.show_open_close_animation(ctx, &frame, is_open); | ||
|
||
backwards_compatibility_open(&mut open, &collapsing); | ||
|
||
if !is_open { | ||
collapsing.store(ctx); | ||
return None; | ||
} | ||
|
||
let area_id = area.id; | ||
let area_layer_id = area.layer(); | ||
let resize_id = area_id.with("resize"); | ||
let mut collapsing = | ||
CollapsingState::load_with_default_open(ctx, area_id.with("collapsing"), default_open); | ||
let area_layer_id = area.layer(); | ||
|
||
let is_collapsed = with_title_bar && !collapsing.is_open(); | ||
|
||
let possible = PossibleInteractions::new(&area, &resize, is_collapsed); | ||
|
||
let area = area.movable(false); // We move it manually, or the area will move the window when we want to resize it | ||
|
@@ -388,6 +452,7 @@ impl<'open> Window<'open> { | |
} else { | ||
None | ||
}; | ||
|
||
let hover_interaction = resize_hover(ctx, possible, area_layer_id, last_frame_outer_rect); | ||
|
||
let mut area_content_ui = area.content_ui(ctx); | ||
|
@@ -397,12 +462,11 @@ impl<'open> Window<'open> { | |
let frame_stroke = frame.stroke; | ||
let mut frame = frame.begin(&mut area_content_ui); | ||
|
||
let show_close_button = open.is_some(); | ||
let title_bar = if with_title_bar { | ||
let title_bar = show_title_bar( | ||
&mut frame.content_ui, | ||
title, | ||
show_close_button, | ||
with_closing_btn, | ||
&mut collapsing, | ||
collapsible, | ||
); | ||
|
@@ -438,7 +502,7 @@ impl<'open> Window<'open> { | |
&mut area_content_ui, | ||
outer_rect, | ||
&content_response, | ||
open, | ||
with_closing_btn, | ||
&mut collapsing, | ||
collapsible, | ||
); | ||
|
@@ -468,11 +532,13 @@ impl<'open> Window<'open> { | |
|
||
let full_response = area.end(ctx, area_content_ui); | ||
|
||
let inner_response = InnerResponse { | ||
// Hide butten could have been pressed | ||
backwards_compatibility_open(&mut open, &collapsing); | ||
|
||
Some(InnerResponse { | ||
inner: content_inner, | ||
response: full_response, | ||
}; | ||
Some(inner_response) | ||
}) | ||
} | ||
} | ||
|
||
|
@@ -907,7 +973,7 @@ impl TitleBar { | |
ui: &mut Ui, | ||
outer_rect: Rect, | ||
content_response: &Option<Response>, | ||
open: Option<&mut bool>, | ||
with_closing_btn: bool, | ||
collapsing: &mut CollapsingState, | ||
collapsible: bool, | ||
) { | ||
|
@@ -916,11 +982,9 @@ impl TitleBar { | |
self.rect.max.x = self.rect.max.x.max(content_response.rect.max.x); | ||
} | ||
|
||
if let Some(open) = open { | ||
// Add close button now that we know our full width: | ||
if self.close_button_ui(ui).clicked() { | ||
*open = false; | ||
} | ||
// Add close button now that we know our full width: | ||
if with_closing_btn && self.close_button_ui(ui).clicked() { | ||
collapsing.toggle_hidden(); | ||
} | ||
|
||
let full_top_rect = Rect::from_x_y_ranges(self.rect.x_range(), self.min_rect.y_range()); | ||
|
@@ -950,7 +1014,7 @@ impl TitleBar { | |
.double_clicked() | ||
&& collapsible | ||
{ | ||
collapsing.toggle(ui); | ||
collapsing.toggle_open(ui); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does it mean for a
CollapsingHeader
to be hidden?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well windows can become hidden, collapsing headers won't do anything but I will update the PR, so this code won't be accessible when not using a window. I also have made the same event option for normal widgets to expand/collapse which I will include in this PR as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some comments to notify the dev that its only for window components. The API is not open to end users, I have made another branch where I split the two structs (see comments below).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I build a split between the two states on this branch (draft): Norlock@40b1fb7#diff-817170c50650af7962ada22bb6459b20eeb54ce921043e4db8522368ccf8933e. I don't know if its a real improvement but you can check it out. If you prefer that version I can clean up the code a bit