Files
galaxy/app/src/view_components/feature_popup.rs
T

175 lines
5.7 KiB
Rust

use galaxyui::elements::{
ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Flex, Hoverable, MouseStateHandle,
ParentElement, Radius, Text,
};
use warpui::platform::Cursor;
use warpui::{AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext};
use crate::appearance::Appearance;
use crate::ui_components::icons::Icon;
pub enum NewFeaturePopupLabel {
/// A static label.
FromString(String),
/// A label that is computed on demand.
FromCallable(Box<dyn Fn(&AppContext) -> String>),
}
pub enum FeaturePopupBadge {
// Displays "NEW" badge prior to the label
New,
// Displays an alert icon prior to the label
AlertIcon,
}
/// A dismissable popup that displays a label indicating that a new feature is available.
pub struct FeaturePopup {
dismiss_mouse_state: MouseStateHandle,
label: NewFeaturePopupLabel,
badge: FeaturePopupBadge,
}
#[derive(Debug, Clone)]
pub enum NewFeaturePopupAction {
Dismiss,
}
impl FeaturePopup {
pub fn new_feature(label: NewFeaturePopupLabel) -> Self {
Self {
dismiss_mouse_state: Default::default(),
label,
badge: FeaturePopupBadge::New,
}
}
pub fn alert_icon(label: NewFeaturePopupLabel) -> Self {
Self {
dismiss_mouse_state: Default::default(),
label,
badge: FeaturePopupBadge::AlertIcon,
}
}
fn render_badge(&self, appearance: &Appearance) -> Box<dyn Element> {
let background = appearance.theme().background();
match self.badge {
FeaturePopupBadge::New => Container::new(
Text::new(
"NEW",
appearance.ui_font_family(),
appearance.ui_font_size(),
)
.with_color(appearance.theme().main_text_color(background).into())
.finish(),
)
.with_vertical_padding(2.)
.with_horizontal_padding(4.)
.with_background_color(
appearance
.theme()
.ansi_bg(appearance.theme().terminal_colors().normal.green),
)
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(2.)))
.finish(),
FeaturePopupBadge::AlertIcon => Container::new(
ConstrainedBox::new(
Icon::AlertCircle
.to_galaxyui_icon(appearance.theme().main_text_color(
appearance.theme().terminal_colors().normal.green.into(),
))
.finish(),
)
.with_height(16.)
.with_width(16.)
.finish(),
)
.finish(),
}
}
}
impl View for FeaturePopup {
fn ui_name() -> &'static str {
"FeaturePopup"
}
fn render(&self, app: &AppContext) -> Box<dyn Element> {
let appearance = Appearance::as_ref(app);
let background = appearance.theme().background();
let new_badge = self.render_badge(appearance);
let label = match &self.label {
NewFeaturePopupLabel::FromString(label) => label.clone(),
NewFeaturePopupLabel::FromCallable(callable) => callable(app),
};
ConstrainedBox::new(
Container::new(
Flex::row()
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_child(Container::new(new_badge).with_margin_right(4.).finish())
.with_child(
Container::new(
Text::new(
label,
appearance.ui_font_family(),
appearance.ui_font_size(),
)
.with_color(background.into())
.finish(),
)
.with_horizontal_padding(4.)
.finish(),
)
.with_child(
Hoverable::new(self.dismiss_mouse_state.clone(), |_| {
ConstrainedBox::new(
Icon::X
.to_galaxyui_icon(appearance.theme().sub_text_color(
appearance.theme().main_text_color(background),
))
.finish(),
)
.with_height(16.)
.with_width(16.)
.finish()
})
.on_click(|ctx, _, _| {
ctx.dispatch_typed_action(NewFeaturePopupAction::Dismiss)
})
.with_cursor(Cursor::PointingHand)
.finish(),
)
.finish(),
)
.with_horizontal_padding(4.)
.with_vertical_padding(4.)
.with_background(appearance.theme().main_text_color(background))
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(4.)))
.finish(),
)
.finish()
}
}
impl TypedActionView for FeaturePopup {
type Action = NewFeaturePopupAction;
fn handle_action(&mut self, action: &Self::Action, ctx: &mut ViewContext<Self>) {
match action {
NewFeaturePopupAction::Dismiss => {
ctx.emit(NewFeaturePopupEvent::Dismissed);
}
}
}
}
#[derive(Debug, Clone)]
pub enum NewFeaturePopupEvent {
Dismissed,
}
impl Entity for FeaturePopup {
type Event = NewFeaturePopupEvent;
}