Skip to content

Commit 7d29266

Browse files
viridiavillor
andauthored
Standard widgets: Popover and Menu. (#21636)
# Objective - Splitting out the non-BSN parts of the work on popup menus. ## Solution - Added Popover and Menu components. ## Testing - Manual testing using example. ## Showcase <img width="311" height="261" alt="popup" src="https://github.com/user-attachments/assets/ab9e8959-65bc-45d3-aa36-9d56cc09ea9d" /> --------- Co-authored-by: Viktor Gustavsson <[email protected]>
1 parent 0fa0b0e commit 7d29266

File tree

8 files changed

+1026
-10
lines changed

8 files changed

+1026
-10
lines changed

crates/bevy_input_focus/src/tab_navigation.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,12 @@ pub struct TabNavigation<'w, 's> {
166166
}
167167

168168
impl TabNavigation<'_, '_> {
169-
/// Navigate to the desired focusable entity.
169+
/// Navigate to the desired focusable entity, relative to the current focused entity.
170170
///
171171
/// Change the [`NavAction`] to navigate in a different direction.
172172
/// Focusable entities are determined by the presence of the [`TabIndex`] component.
173173
///
174-
/// If no focusable entities are found, then this function will return either the first
174+
/// If there is no currently focused entity, then this function will return either the first
175175
/// or last focusable entity, depending on the direction of navigation. For example, if
176176
/// `action` is `Next` and no focusable entities are found, then this function will return
177177
/// the first focusable entity.
@@ -198,13 +198,46 @@ impl TabNavigation<'_, '_> {
198198
})
199199
});
200200

201+
self.navigate_internal(focus.0, action, tabgroup)
202+
}
203+
204+
/// Initialize focus to a focusable child of a container, either the first or last
205+
/// depending on [`NavAction`]. This assumes that the parent entity has a [`TabGroup`]
206+
/// component.
207+
///
208+
/// Focusable entities are determined by the presence of the [`TabIndex`] component.
209+
pub fn initialize(
210+
&self,
211+
parent: Entity,
212+
action: NavAction,
213+
) -> Result<Entity, TabNavigationError> {
214+
// If there are no tab groups, then there are no focusable entities.
215+
if self.tabgroup_query.is_empty() {
216+
return Err(TabNavigationError::NoTabGroups);
217+
}
218+
219+
// Look for the tab group on the parent entity.
220+
match self.tabgroup_query.get(parent) {
221+
Ok(tabgroup) => self.navigate_internal(None, action, Some((parent, tabgroup.1))),
222+
Err(_) => Err(TabNavigationError::NoTabGroups),
223+
}
224+
}
225+
226+
pub fn navigate_internal(
227+
&self,
228+
focus: Option<Entity>,
229+
action: NavAction,
230+
tabgroup: Option<(Entity, &TabGroup)>,
231+
) -> Result<Entity, TabNavigationError> {
201232
let navigation_result = self.navigate_in_group(tabgroup, focus, action);
202233

203234
match navigation_result {
204235
Ok(entity) => {
205-
if focus.0.is_some() && tabgroup.is_none() {
236+
if let Some(previous_focus) = focus
237+
&& tabgroup.is_none()
238+
{
206239
Err(TabNavigationError::NoTabGroupForCurrentFocus {
207-
previous_focus: focus.0.unwrap(),
240+
previous_focus,
208241
new_focus: entity,
209242
})
210243
} else {
@@ -218,7 +251,7 @@ impl TabNavigation<'_, '_> {
218251
fn navigate_in_group(
219252
&self,
220253
tabgroup: Option<(Entity, &TabGroup)>,
221-
focus: &InputFocus,
254+
focus: Option<Entity>,
222255
action: NavAction,
223256
) -> Result<Entity, TabNavigationError> {
224257
// List of all focusable entities found.
@@ -268,7 +301,7 @@ impl TabNavigation<'_, '_> {
268301
}
269302
});
270303

271-
let index = focusable.iter().position(|e| Some(e.0) == focus.0);
304+
let index = focusable.iter().position(|e| Some(e.0) == focus);
272305
let count = focusable.len();
273306
let next = match (index, action) {
274307
(Some(idx), NavAction::Next) => (idx + 1).rem_euclid(count),

crates/bevy_math/src/rects/rect.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,20 @@ impl Rect {
356356
}
357357
}
358358

359+
/// Return the area of this rectangle.
360+
///
361+
/// # Examples
362+
///
363+
/// ```
364+
/// # use bevy_math::Rect;
365+
/// let r = Rect::new(0., 0., 10., 10.); // w=10 h=10
366+
/// assert_eq!(r.area(), 100.0);
367+
/// ```
368+
#[inline]
369+
pub fn area(&self) -> f32 {
370+
self.width() * self.height()
371+
}
372+
359373
/// Returns self as [`IRect`] (i32)
360374
#[inline]
361375
pub fn as_irect(&self) -> IRect {

crates/bevy_ui_widgets/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ keywords = ["bevy"]
1212
# bevy
1313
bevy_app = { path = "../bevy_app", version = "0.18.0-dev" }
1414
bevy_a11y = { path = "../bevy_a11y", version = "0.18.0-dev" }
15+
bevy_camera = { path = "../bevy_camera", version = "0.18.0-dev" }
1516
bevy_ecs = { path = "../bevy_ecs", version = "0.18.0-dev" }
1617
bevy_input = { path = "../bevy_input", version = "0.18.0-dev" }
1718
bevy_input_focus = { path = "../bevy_input_focus", version = "0.18.0-dev" }

crates/bevy_ui_widgets/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@
2020
2121
mod button;
2222
mod checkbox;
23+
mod menu;
2324
mod observe;
25+
pub mod popover;
2426
mod radio;
2527
mod scrollbar;
2628
mod slider;
2729

2830
pub use button::*;
2931
pub use checkbox::*;
32+
pub use menu::*;
3033
pub use observe::*;
3134
pub use radio::*;
3235
pub use scrollbar::*;
@@ -35,15 +38,19 @@ pub use slider::*;
3538
use bevy_app::{PluginGroup, PluginGroupBuilder};
3639
use bevy_ecs::{entity::Entity, event::EntityEvent};
3740

41+
use crate::popover::PopoverPlugin;
42+
3843
/// A plugin group that registers the observers for all of the widgets in this crate. If you don't want to
3944
/// use all of the widgets, you can import the individual widget plugins instead.
4045
pub struct UiWidgetsPlugins;
4146

4247
impl PluginGroup for UiWidgetsPlugins {
4348
fn build(self) -> PluginGroupBuilder {
4449
PluginGroupBuilder::start::<Self>()
50+
.add(PopoverPlugin)
4551
.add(ButtonPlugin)
4652
.add(CheckboxPlugin)
53+
.add(MenuPlugin)
4754
.add(RadioGroupPlugin)
4855
.add(ScrollbarPlugin)
4956
.add(SliderPlugin)

0 commit comments

Comments
 (0)