Add icon position support to buttons
This commit is contained in:
+43
-4
@@ -106,6 +106,16 @@ pub trait ButtonVariants: Sized {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Which side of the label the icon sits on.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
pub enum IconPosition {
|
||||||
|
/// Left of the label.
|
||||||
|
#[default]
|
||||||
|
Start,
|
||||||
|
/// Right of the label.
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
/// A Button element.
|
/// A Button element.
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
@@ -125,6 +135,7 @@ pub struct Button {
|
|||||||
on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>>,
|
on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>>,
|
||||||
tab_stop: bool,
|
tab_stop: bool,
|
||||||
selected: bool,
|
selected: bool,
|
||||||
|
icon_position: IconPosition,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Button> for AnyElement {
|
impl From<Button> for AnyElement {
|
||||||
@@ -151,6 +162,7 @@ impl Button {
|
|||||||
loading: false,
|
loading: false,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
tab_stop: true,
|
tab_stop: true,
|
||||||
|
icon_position: IconPosition::Start,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,6 +183,12 @@ impl Button {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set which side of the label the icon sits on (default [`IconPosition::Start`]).
|
||||||
|
pub fn icon_position(mut self, position: IconPosition) -> Self {
|
||||||
|
self.icon_position = position;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tooltip(mut self, tooltip: impl Into<SharedString>) -> Self {
|
pub fn tooltip(mut self, tooltip: impl Into<SharedString>) -> Self {
|
||||||
self.tooltip = Some(tooltip.into());
|
self.tooltip = Some(tooltip.into());
|
||||||
self
|
self
|
||||||
@@ -272,6 +290,7 @@ impl RenderOnce for Button {
|
|||||||
Size::Large => Size::Medium,
|
Size::Large => Size::Medium,
|
||||||
_ => self.size,
|
_ => self.size,
|
||||||
};
|
};
|
||||||
|
let icon_start = self.icon_position == IconPosition::Start;
|
||||||
|
|
||||||
self.base
|
self.base
|
||||||
.tab_index(0)
|
.tab_index(0)
|
||||||
@@ -318,7 +337,7 @@ impl RenderOnce for Button {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Normal Button
|
// Normal Button
|
||||||
match self.size {
|
let this = match self.size {
|
||||||
Size::Size(size) => this.px(size * 0.2),
|
Size::Size(size) => this.px(size * 0.2),
|
||||||
Size::XSmall => {
|
Size::XSmall => {
|
||||||
if self.icon.is_some() {
|
if self.icon.is_some() {
|
||||||
@@ -348,6 +367,16 @@ impl RenderOnce for Button {
|
|||||||
this.h_10().px_3()
|
this.h_10().px_3()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !icon_start && self.icon.is_some() {
|
||||||
|
match self.size {
|
||||||
|
Size::XSmall | Size::Small => this.pl_2p5().pr_2(),
|
||||||
|
Size::Medium | Size::Large => this.pl_3p5().pr_3(),
|
||||||
|
Size::Size(_) => this,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -369,16 +398,26 @@ impl RenderOnce for Button {
|
|||||||
Size::Small => this.text_style(TextStyle::Callout).gap_1p5(),
|
Size::Small => this.text_style(TextStyle::Callout).gap_1p5(),
|
||||||
_ => this.text_style(TextStyle::Callout).gap(px(theme::SPACE)),
|
_ => this.text_style(TextStyle::Callout).gap(px(theme::SPACE)),
|
||||||
})
|
})
|
||||||
.when(!self.loading, |this| {
|
.when(!self.loading && icon_start, |this| {
|
||||||
this.when_some(self.icon, |this, icon| {
|
this.when_some(self.icon.clone(), |this, icon| {
|
||||||
this.child(icon.with_size(icon_size))
|
this.child(icon.with_size(icon_size))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.when(self.loading, |this| this.child(Indicator::new()))
|
.when(self.loading && icon_start, |this| {
|
||||||
|
this.child(Indicator::new())
|
||||||
|
})
|
||||||
.when_some(self.label, |this, label| {
|
.when_some(self.label, |this, label| {
|
||||||
this.child(div().flex_none().line_height(relative(1.)).child(label))
|
this.child(div().flex_none().line_height(relative(1.)).child(label))
|
||||||
})
|
})
|
||||||
.children(self.children)
|
.children(self.children)
|
||||||
|
.when(!self.loading && !icon_start, |this| {
|
||||||
|
this.when_some(self.icon.clone(), |this, icon| {
|
||||||
|
this.child(icon.with_size(icon_size))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.when(self.loading && !icon_start, |this| {
|
||||||
|
this.child(Indicator::new())
|
||||||
|
})
|
||||||
.when(self.caret, |this| {
|
.when(self.caret, |this| {
|
||||||
this.justify_between().gap_0p5().child(
|
this.justify_between().gap_0p5().child(
|
||||||
Icon::new(IconName::ChevronDown)
|
Icon::new(IconName::ChevronDown)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use gpui::{App, AppContext, ClickEvent, ParentElement, Styled, Window, px};
|
use gpui::{App, AppContext, ClickEvent, ParentElement, Styled, Window, px};
|
||||||
use nip29::{GroupReference, GroupsRegistry};
|
use nip29::{GroupReference, GroupsRegistry};
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants, IconPosition};
|
||||||
use ui::input::{Input, InputState};
|
use ui::input::{Input, InputState};
|
||||||
use ui::notification::Notification;
|
use ui::notification::Notification;
|
||||||
use ui::{IconName, StyledExt, WindowExtension, h_flex, v_flex};
|
use ui::{IconName, StyledExt, WindowExtension, h_flex, v_flex};
|
||||||
@@ -56,7 +56,8 @@ pub fn open(window: &mut Window, cx: &mut App) {
|
|||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("join")
|
Button::new("join")
|
||||||
.icon(IconName::CheckCircle)
|
.icon(IconName::ArrowRight)
|
||||||
|
.icon_position(IconPosition::End)
|
||||||
.label("Join")
|
.label("Join")
|
||||||
.primary()
|
.primary()
|
||||||
.font_semibold()
|
.font_semibold()
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use settings::AppSettings;
|
|||||||
use state::NostrRegistry;
|
use state::NostrRegistry;
|
||||||
use theme::{ActiveTheme, TextStyle, Typeset as _};
|
use theme::{ActiveTheme, TextStyle, Typeset as _};
|
||||||
use ui::avatar::Avatar;
|
use ui::avatar::Avatar;
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants, IconPosition};
|
||||||
use ui::dock::{DockArea, DockPlacement, PanelHandle};
|
use ui::dock::{DockArea, DockPlacement, PanelHandle};
|
||||||
use ui::input::{Input, InputEvent, InputState};
|
use ui::input::{Input, InputEvent, InputState};
|
||||||
use ui::nav::Nav;
|
use ui::nav::Nav;
|
||||||
@@ -412,7 +412,12 @@ impl Render for NewChat {
|
|||||||
})
|
})
|
||||||
.map(|this| {
|
.map(|this| {
|
||||||
if let Some(error) = self.error.clone() {
|
if let Some(error) = self.error.clone() {
|
||||||
this.child(div().text_style(TextStyle::Caption).text_color(cx.theme().danger).child(error))
|
this.child(
|
||||||
|
div()
|
||||||
|
.text_style(TextStyle::Caption)
|
||||||
|
.text_color(cx.theme().danger)
|
||||||
|
.child(error),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
this.child(
|
this.child(
|
||||||
div()
|
div()
|
||||||
@@ -444,7 +449,8 @@ impl Render for NewChat {
|
|||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("start")
|
Button::new("start")
|
||||||
.icon(IconName::CheckCircle)
|
.icon(IconName::ArrowRight)
|
||||||
|
.icon_position(IconPosition::End)
|
||||||
.label("Start")
|
.label("Start")
|
||||||
.primary()
|
.primary()
|
||||||
.font_semibold()
|
.font_semibold()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use gpui::{App, AppContext, ClickEvent, Div, Entity, ParentElement, Styled, Wind
|
|||||||
use nip29::{GroupId, GroupKey, GroupMetadata, GroupsRegistry};
|
use nip29::{GroupId, GroupKey, GroupMetadata, GroupsRegistry};
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use theme::{ActiveTheme, TextStyle, Typeset as _};
|
use theme::{ActiveTheme, TextStyle, Typeset as _};
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants, IconPosition};
|
||||||
use ui::input::{Input, InputState};
|
use ui::input::{Input, InputState};
|
||||||
use ui::notification::Notification;
|
use ui::notification::Notification;
|
||||||
use ui::{IconName, StyledExt, WindowExtension, h_flex, v_flex};
|
use ui::{IconName, StyledExt, WindowExtension, h_flex, v_flex};
|
||||||
@@ -47,7 +47,8 @@ pub fn open(window: &mut Window, cx: &mut App) {
|
|||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("create")
|
Button::new("create")
|
||||||
.icon(IconName::CheckCircle)
|
.icon(IconName::ArrowRight)
|
||||||
|
.icon_position(IconPosition::End)
|
||||||
.label("Create")
|
.label("Create")
|
||||||
.primary()
|
.primary()
|
||||||
.font_semibold()
|
.font_semibold()
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use settings::AppSettings;
|
|||||||
use state::{NostrRegistry, upload};
|
use state::{NostrRegistry, upload};
|
||||||
use theme::{ActiveTheme, TextStyle, Typeset as _, button_radius};
|
use theme::{ActiveTheme, TextStyle, Typeset as _, button_radius};
|
||||||
use ui::avatar::Avatar;
|
use ui::avatar::Avatar;
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants, IconPosition};
|
||||||
use ui::dock::DockPlacement;
|
use ui::dock::DockPlacement;
|
||||||
use ui::input::{Input, InputState, Textarea, TextareaState};
|
use ui::input::{Input, InputState, Textarea, TextareaState};
|
||||||
use ui::notification::Notification;
|
use ui::notification::Notification;
|
||||||
@@ -371,7 +371,8 @@ impl Render for ProfileDialog {
|
|||||||
.when(self.editing, |this| {
|
.when(self.editing, |this| {
|
||||||
this.child(
|
this.child(
|
||||||
Button::new("upload")
|
Button::new("upload")
|
||||||
.icon(IconName::PlusCircle)
|
.icon(IconName::ArrowRight)
|
||||||
|
.icon_position(IconPosition::End)
|
||||||
.label("Add an avatar")
|
.label("Add an avatar")
|
||||||
.xsmall()
|
.xsmall()
|
||||||
.ghost()
|
.ghost()
|
||||||
@@ -464,6 +465,7 @@ impl Render for ProfileDialog {
|
|||||||
this.child(
|
this.child(
|
||||||
Button::new("update")
|
Button::new("update")
|
||||||
.icon(IconName::Edit)
|
.icon(IconName::Edit)
|
||||||
|
.icon_position(IconPosition::End)
|
||||||
.label("Update")
|
.label("Update")
|
||||||
.primary()
|
.primary()
|
||||||
.font_semibold()
|
.font_semibold()
|
||||||
@@ -476,6 +478,7 @@ impl Render for ProfileDialog {
|
|||||||
this.child(
|
this.child(
|
||||||
Button::new("message")
|
Button::new("message")
|
||||||
.icon(IconName::Message)
|
.icon(IconName::Message)
|
||||||
|
.icon_position(IconPosition::End)
|
||||||
.label("Message")
|
.label("Message")
|
||||||
.primary()
|
.primary()
|
||||||
.font_semibold()
|
.font_semibold()
|
||||||
@@ -497,6 +500,7 @@ impl Render for ProfileDialog {
|
|||||||
.child(
|
.child(
|
||||||
Button::new("save")
|
Button::new("save")
|
||||||
.icon(IconName::CheckCircle)
|
.icon(IconName::CheckCircle)
|
||||||
|
.icon_position(IconPosition::End)
|
||||||
.label("Save")
|
.label("Save")
|
||||||
.primary()
|
.primary()
|
||||||
.font_semibold()
|
.font_semibold()
|
||||||
@@ -530,8 +534,10 @@ fn field(
|
|||||||
None => (SharedString::from(placeholder), cx.theme().text_muted),
|
None => (SharedString::from(placeholder), cx.theme().text_muted),
|
||||||
};
|
};
|
||||||
|
|
||||||
v_flex()
|
v_flex().gap_1p5().child(label(text, cx)).child(
|
||||||
.gap_1p5()
|
div()
|
||||||
.child(label(text, cx))
|
.text_style(TextStyle::Callout)
|
||||||
.child(div().text_style(TextStyle::Callout).text_color(color).child(content))
|
.text_color(color)
|
||||||
|
.child(content),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use nostr_sdk::prelude::*;
|
|||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use state::NostrRegistry;
|
use state::NostrRegistry;
|
||||||
use theme::{ActiveTheme, TextStyle, Typeset as _, button_radius, panel_radius};
|
use theme::{ActiveTheme, TextStyle, Typeset as _, button_radius, panel_radius};
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants, IconPosition};
|
||||||
use ui::input::{Input, InputEvent, InputState};
|
use ui::input::{Input, InputEvent, InputState};
|
||||||
use ui::menu::{DropdownMenu, PopupMenuItem};
|
use ui::menu::{DropdownMenu, PopupMenuItem};
|
||||||
use ui::scroll::ScrollableElement;
|
use ui::scroll::ScrollableElement;
|
||||||
@@ -283,7 +283,8 @@ impl RelayManager {
|
|||||||
|
|
||||||
h_flex().gap_1().justify_end().child(
|
h_flex().gap_1().justify_end().child(
|
||||||
Button::new("update")
|
Button::new("update")
|
||||||
.icon(IconName::CheckCircle)
|
.icon(IconName::ArrowRight)
|
||||||
|
.icon_position(IconPosition::End)
|
||||||
.label("Update")
|
.label("Update")
|
||||||
.primary()
|
.primary()
|
||||||
.font_semibold()
|
.font_semibold()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use instant::Duration;
|
|||||||
use nip4e::DeviceRegistry;
|
use nip4e::DeviceRegistry;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use theme::{ActiveTheme, TextStyle, Typeset as _};
|
use theme::{ActiveTheme, TextStyle, Typeset as _};
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants, IconPosition};
|
||||||
use ui::input::{Input, InputEvent, InputState};
|
use ui::input::{Input, InputEvent, InputState};
|
||||||
use ui::{IconName, StyledExt, WindowExtension, h_flex, v_flex};
|
use ui::{IconName, StyledExt, WindowExtension, h_flex, v_flex};
|
||||||
|
|
||||||
@@ -112,7 +112,8 @@ impl Render for RestoreEncryption {
|
|||||||
.child(
|
.child(
|
||||||
h_flex().gap_1().justify_end().child(
|
h_flex().gap_1().justify_end().child(
|
||||||
Button::new("restore")
|
Button::new("restore")
|
||||||
.icon(IconName::CheckCircle)
|
.icon(IconName::ArrowRight)
|
||||||
|
.icon_position(IconPosition::End)
|
||||||
.label("Restore")
|
.label("Restore")
|
||||||
.primary()
|
.primary()
|
||||||
.font_semibold()
|
.font_semibold()
|
||||||
|
|||||||
@@ -389,7 +389,10 @@ impl Render for Screening {
|
|||||||
.text_style(TextStyle::Callout)
|
.text_style(TextStyle::Callout)
|
||||||
.child(status_badge(Some(self.followed), cx))
|
.child(status_badge(Some(self.followed), cx))
|
||||||
.child(
|
.child(
|
||||||
v_flex().text_style(TextStyle::Callout).child("Contact").child(
|
v_flex()
|
||||||
|
.text_style(TextStyle::Callout)
|
||||||
|
.child("Contact")
|
||||||
|
.child(
|
||||||
div()
|
div()
|
||||||
.line_clamp(1)
|
.line_clamp(1)
|
||||||
.text_color(cx.theme().text_muted)
|
.text_color(cx.theme().text_muted)
|
||||||
|
|||||||
Reference in New Issue
Block a user