Skip to content

Commit

Permalink
Fix and improve various things
Browse files Browse the repository at this point in the history
  • Loading branch information
benfrankel committed Aug 20, 2024
1 parent 65c72e9 commit d5f344a
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 76 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- **Initial release**
- Added `TooltipPlugin` plugin
- Added `TooltipSet` system set
- Added `PrimaryTooltip` resource
- Added `Tooltip` component
- Added `bevy_reflect` feature
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ bevy_ui = { version = "0.14", default-features = false, features = [
"bevy_text",
] }
bevy_window = { version = "0.14", default-features = false }
tiny_bail = "0.2"
tiny_bail = "0.3"

[lints.rust]
missing_docs = "deny"
Expand Down
50 changes: 22 additions & 28 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use bevy_ui::{Interaction, UiStack};
use bevy_window::{PrimaryWindow, Window, WindowRef};
use tiny_bail::prelude::*;

use crate::{PrimaryTooltip, Tooltip, TooltipActivation, TooltipEntity, TooltipTransfer};
use crate::{PrimaryTooltip, Tooltip, TooltipEntity, TooltipSet};

pub(super) fn plugin(app: &mut App) {
app.register_type::<TooltipContext>();
Expand All @@ -33,7 +33,8 @@ pub(super) fn plugin(app: &mut App) {
hide_tooltip.run_if(on_event::<HideTooltip>()),
show_tooltip.run_if(on_event::<ShowTooltip>()),
)
.chain(),
.chain()
.in_set(TooltipSet::Content),
);
}

Expand All @@ -53,12 +54,8 @@ pub(crate) struct TooltipContext {
timer: u16,
/// The current cursor position or activation point.
pub(crate) cursor_pos: Vec2,
/// The current activation conditions.
activation: TooltipActivation,
/// The current transfer conditions.
transfer: TooltipTransfer,
/// The tooltip container entity.
entity: TooltipEntity,
/// The current tooltip parameters.
pub(crate) tooltip: Tooltip,
}

impl Default for TooltipContext {
Expand All @@ -68,9 +65,7 @@ impl Default for TooltipContext {
target: Entity::PLACEHOLDER,
timer: 0,
cursor_pos: Vec2::ZERO,
activation: TooltipActivation::IMMEDIATE,
transfer: TooltipTransfer::NONE,
entity: TooltipEntity::Custom(Entity::PLACEHOLDER),
tooltip: Tooltip::custom(Entity::PLACEHOLDER),
}
}
}
Expand All @@ -89,7 +84,7 @@ fn update_tooltip_context(
) {
let old_active = matches!(ctx.state, TooltipState::Active);
let old_target = ctx.target;
let old_entity = match ctx.entity {
let old_entity = match ctx.tooltip.entity {
TooltipEntity::Primary(_) => primary.container,
TooltipEntity::Custom(id) => id,
};
Expand All @@ -111,14 +106,14 @@ fn update_tooltip_context(
// Reset activation delay on cursor move.
if ctx.cursor_pos != cursor_pos
&& matches!(ctx.state, TooltipState::Delayed)
&& ctx.activation.reset_delay_on_cursor_move
&& ctx.tooltip.activation.reset_delay_on_cursor_move
{
ctx.timer = ctx.activation.delay;
ctx.timer = ctx.tooltip.activation.delay;
}

// Dismiss tooltip if cursor has left the activation radius.
if matches!(ctx.state, TooltipState::Active)
&& ctx.cursor_pos.distance_squared(cursor_pos) > ctx.activation.dismiss_radius
&& ctx.cursor_pos.distance_squared(cursor_pos) > ctx.tooltip.activation.dismiss_radius
{
ctx.state = TooltipState::Dismissed;
}
Expand Down Expand Up @@ -147,7 +142,7 @@ fn update_tooltip_context(
Interaction::Pressed => {
ctx.target = entity;
ctx.state = TooltipState::Dismissed;
ctx.transfer = tooltip.transfer;
ctx.tooltip.transfer = tooltip.transfer;
found_target = true;
break;
}
Expand All @@ -164,30 +159,29 @@ fn update_tooltip_context(
ctx.state = if tooltip.activation.delay == 0
|| (matches!(ctx.state, TooltipState::Inactive)
&& ctx.timer > 0
&& ctx.transfer.layer >= tooltip.transfer.layer
&& (matches!((ctx.transfer.group, tooltip.transfer.group), (Some(x), Some(y)) if x == y)
&& ctx.tooltip.transfer.layer >= tooltip.transfer.layer
&& (matches!((ctx.tooltip.transfer.group, tooltip.transfer.group), (Some(x), Some(y)) if x == y)
|| ctx.target == entity))
{
TooltipState::Active
} else {
TooltipState::Delayed
};
ctx.timer = tooltip.activation.delay;
ctx.activation = tooltip.activation;
ctx.activation.dismiss_radius *= ctx.activation.dismiss_radius;
ctx.transfer = tooltip.transfer;
ctx.entity = tooltip.entity.clone();
ctx.tooltip = tooltip.clone();
ctx.tooltip.activation.dismiss_radius *= ctx.tooltip.activation.dismiss_radius;
found_target = true;
break;
}

// There is no longer a target entity.
if !found_target && !matches!(ctx.state, TooltipState::Inactive) {
ctx.timer = if matches!(ctx.state, TooltipState::Active) || !ctx.transfer.from_active {
ctx.transfer.timeout
} else {
0
};
ctx.timer =
if matches!(ctx.state, TooltipState::Active) || !ctx.tooltip.transfer.from_active {
ctx.tooltip.transfer.timeout
} else {
0
};
ctx.state = TooltipState::Inactive;
}

Expand Down Expand Up @@ -240,7 +234,7 @@ fn show_tooltip(
mut text_query: Query<&mut Text>,
mut visibility_query: Query<&mut Visibility>,
) {
let entity = match &mut ctx.entity {
let entity = match &mut ctx.tooltip.entity {
TooltipEntity::Primary(ref mut text) => {
if let Ok(mut primary_text) = text_query.get_mut(primary.text) {
*primary_text = std::mem::take(text);
Expand Down
88 changes: 70 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,40 +44,47 @@ mod placement;
pub mod prelude {
pub use super::{
PrimaryTooltip, Tooltip, TooltipActivation, TooltipEntity, TooltipPlacement, TooltipPlugin,
TooltipTransfer,
TooltipSet, TooltipTransfer,
};
}

use bevy_app::Plugin;
use bevy_app::{Plugin, PostUpdate, PreUpdate};
use bevy_color::Color;
use bevy_core::Name;
#[cfg(feature = "bevy_reflect")]
use bevy_ecs::reflect::{ReflectComponent, ReflectResource};
use bevy_ecs::{component::Component, entity::Entity, system::Resource, world::World};
use bevy_ecs::{
component::Component,
entity::Entity,
schedule::{IntoSystemSetConfigs as _, SystemSet},
system::Resource,
world::World,
};
use bevy_hierarchy::BuildWorldChildren as _;
use bevy_render::view::Visibility;
use bevy_text::{Text, TextSection, TextStyle};
use bevy_text::{JustifyText, Text, TextSection, TextStyle};
use bevy_transform::TransformSystem;
use bevy_ui::{
node_bundles::{NodeBundle, TextBundle},
PositionType, Style, UiRect, Val, ZIndex,
PositionType, Style, UiRect, UiSystem, Val, ZIndex,
};

pub use placement::TooltipPlacement;

/// A [`Plugin`] that sets up the tooltip widget system.
#[derive(Default)]
pub struct TooltipPlugin {
/// Set a custom entity for [`PrimaryTooltip::container`], or spawn the default container
/// entity if `None`.
///
/// This entity should include all of the components of [`NodeBundle`], with
/// [`Visibility::Hidden`] and [`Style::position_type`] set to [`PositionType::Absolute`].
pub container: Option<Entity>,
pub container: Entity,
/// Set a custom entity for [`PrimaryTooltip::text`], or spawn the default text entity if
/// `None`.
///
/// This entity should include all of the components of [`TextBundle`].
pub text: Option<Entity>,
/// This entity should include all of the components of [`TextBundle`] and be a child of
/// [`Self::container`].
pub text: Entity,
}

impl Plugin for TooltipPlugin {
Expand All @@ -88,10 +95,29 @@ impl Plugin for TooltipPlugin {

app.register_type::<Tooltip>();

app.configure_sets(PreUpdate, (UiSystem::Focus, TooltipSet::Content).chain());
app.configure_sets(
PostUpdate,
(
UiSystem::Layout,
TooltipSet::Placement,
TransformSystem::TransformPropagate,
)
.chain(),
);
app.add_plugins((context::plugin, placement::plugin));
}
}

impl Default for TooltipPlugin {
fn default() -> Self {
Self {
container: Entity::PLACEHOLDER,
text: Entity::PLACEHOLDER,
}
}
}

/// A [`Resource`] containing the [`Entity`] IDs of the global primary tooltip.
///
/// See [`TooltipPlugin`] to set up a custom primary tooltip.
Expand All @@ -109,14 +135,17 @@ pub struct PrimaryTooltip {
}

impl PrimaryTooltip {
fn new(world: &mut World, container: Option<Entity>, text: Option<Entity>) -> Self {
let container = container.unwrap_or_else(|| {
fn new(world: &mut World, container: Entity, text: Entity) -> Self {
let container = if container != Entity::PLACEHOLDER {
container
} else {
world
.spawn((
Name::new("PrimaryTooltip"),
NodeBundle {
style: Style {
position_type: PositionType::Absolute,
max_width: Val::Vw(40.0),
padding: UiRect::all(Val::Px(8.0)),
..Default::default()
},
Expand All @@ -127,14 +156,16 @@ impl PrimaryTooltip {
},
))
.id()
});
};

let text = text.unwrap_or_else(|| {
let text = if text != Entity::PLACEHOLDER {
text
} else {
world
.spawn((Name::new("Text"), TextBundle::default()))
.set_parent(container)
.id()
});
};

Self { container, text }
}
Expand All @@ -151,7 +182,7 @@ impl PrimaryTooltip {
/// - [`TooltipActivation::IDLE`]
/// - [`TooltipTransfer::NONE`]
/// - [`TooltipPlacement::CURSOR`]
#[derive(Component)]
#[derive(Component, Clone, Debug)]
#[cfg_attr(
feature = "bevy_reflect",
derive(bevy_reflect::Reflect),
Expand Down Expand Up @@ -179,6 +210,11 @@ impl Tooltip {
}
}

/// Use a custom tooltip entity and default behavior.
pub fn custom(entity: Entity) -> Self {
Self::new(TooltipEntity::Custom(entity))
}

/// Use the primary tooltip entity with a single [`TextSection`] and default behavior.
pub fn from_section(value: impl Into<String>, style: TextStyle) -> Self {
Self::new(TooltipEntity::Primary(Text::from_section(value, style)))
Expand All @@ -194,9 +230,16 @@ impl Tooltip {
Self::new(TooltipEntity::Primary(text.into()))
}

/// Use a custom tooltip entity and default behavior.
pub fn custom(entity: Entity) -> Self {
Self::new(TooltipEntity::Custom(entity))
/// Set the [`JustifyText`].
///
/// NOTE: This does nothing with a custom tooltip.
pub fn with_justify_text(mut self, justify_text: JustifyText) -> Self {
match &mut self.entity {
TooltipEntity::Primary(text) => text.justify = justify_text,
// TODO: Warn?
_ => (),
}
self
}

/// Set a custom [`TooltipActivation`].
Expand Down Expand Up @@ -341,3 +384,12 @@ pub enum TooltipEntity {
/// Use a fully custom entity as the tooltip.
Custom(Entity),
}

/// A [`SystemSet`] for tooltip systems.
#[derive(SystemSet, Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum TooltipSet {
/// Update and show / hide the tooltip content (runs in [`PreUpdate`]).
Content,
/// Position the tooltip using its calculated size (runs in [`PostUpdate`]).
Placement,
}
Loading

0 comments on commit d5f344a

Please sign in to comment.