text_view: Update Switch, Checkbox, Radio to let label method support TextView. (#649)
- Add `style` method to TextView. - Add `inline` method to TextView to use default inline style.
This commit is contained in:
parent
9dade704fb
commit
8fa210bc21
10 changed files with 213 additions and 60 deletions
35
crates/story/examples/switch.rs
Normal file
35
crates/story/examples/switch.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
use gpui::*;
|
||||
use story::{Assets, SwitchStory};
|
||||
|
||||
pub struct Example {
|
||||
story: Entity<SwitchStory>,
|
||||
}
|
||||
|
||||
impl Example {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let story = SwitchStory::view(window, cx);
|
||||
|
||||
Self { story }
|
||||
}
|
||||
|
||||
fn view(window: &mut Window, cx: &mut App) -> Entity<Self> {
|
||||
cx.new(|cx| Self::new(window, cx))
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Example {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
div().p_4().size_full().child(self.story.clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let app = Application::new().with_assets(Assets);
|
||||
|
||||
app.run(move |cx| {
|
||||
story::init(cx);
|
||||
cx.activate(true);
|
||||
|
||||
story::create_new_window("Switch Example", Example::view, cx);
|
||||
});
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ use gpui_component::{
|
|||
label::Label,
|
||||
radio::{Radio, RadioGroup},
|
||||
switch::Switch,
|
||||
text::TextView,
|
||||
v_flex, ActiveTheme, Disableable as _, Side, Sizable, StyledExt,
|
||||
};
|
||||
|
||||
|
|
@ -203,12 +204,18 @@ impl Render for SwitchStory {
|
|||
v.check3 = !v.check3;
|
||||
})),
|
||||
)
|
||||
.child(div().w(px(300.)).child(
|
||||
Checkbox::new("longlong-checkbox").label(
|
||||
"The long long label text, \
|
||||
it should ellipsis when the text is too long.",
|
||||
.child(
|
||||
div().w(px(300.)).child(
|
||||
Checkbox::new("longlong-checkbox").label(
|
||||
TextView::markdown(
|
||||
"longlong-checkbox",
|
||||
"The **long long label** text, \
|
||||
it should ellipsis when the text is too long.",
|
||||
)
|
||||
.inline(),
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
use crate::{h_flex, v_flex, ActiveTheme, Disableable, IconName, Selectable};
|
||||
use crate::{h_flex, text::Text, v_flex, ActiveTheme, Disableable, IconName, Selectable};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, relative, svg, App, ElementId, InteractiveElement,
|
||||
IntoElement, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement as _,
|
||||
Styled as _, Window,
|
||||
IntoElement, ParentElement, RenderOnce, StatefulInteractiveElement as _, Styled as _, Window,
|
||||
};
|
||||
|
||||
/// A Checkbox element.
|
||||
#[derive(IntoElement)]
|
||||
pub struct Checkbox {
|
||||
id: ElementId,
|
||||
label: Option<SharedString>,
|
||||
label: Option<Text>,
|
||||
checked: bool,
|
||||
disabled: bool,
|
||||
on_click: Option<Box<dyn Fn(&bool, &mut Window, &mut App) + 'static>>,
|
||||
|
|
@ -26,7 +25,7 @@ impl Checkbox {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
||||
pub fn label(mut self, label: impl Into<Text>) -> Self {
|
||||
self.label = Some(label.into());
|
||||
self
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use crate::{h_flex, v_flex, ActiveTheme, AxisExt, IconName};
|
||||
use crate::{h_flex, text::Text, v_flex, ActiveTheme, AxisExt, IconName};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, relative, svg, App, Axis, ElementId, InteractiveElement,
|
||||
IntoElement, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, Styled,
|
||||
|
|
@ -13,7 +13,7 @@ use gpui::{
|
|||
#[derive(IntoElement)]
|
||||
pub struct Radio {
|
||||
id: ElementId,
|
||||
label: Option<SharedString>,
|
||||
label: Option<Text>,
|
||||
checked: bool,
|
||||
disabled: bool,
|
||||
on_click: Option<Box<dyn Fn(&bool, &mut Window, &mut App) + 'static>>,
|
||||
|
|
@ -30,7 +30,7 @@ impl Radio {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
||||
pub fn label(mut self, label: impl Into<Text>) -> Self {
|
||||
self.label = Some(label.into());
|
||||
self
|
||||
}
|
||||
|
|
@ -114,24 +114,6 @@ impl RenderOnce for Radio {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for Radio {
|
||||
fn from(label: &'static str) -> Self {
|
||||
Self::new(label).label(label)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SharedString> for Radio {
|
||||
fn from(label: SharedString) -> Self {
|
||||
Self::new(label.clone()).label(label)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Radio {
|
||||
fn from(label: String) -> Self {
|
||||
Self::new(SharedString::from(label.clone())).label(SharedString::from(label))
|
||||
}
|
||||
}
|
||||
|
||||
/// A Radio group element.
|
||||
#[derive(IntoElement)]
|
||||
pub struct RadioGroup {
|
||||
|
|
@ -200,6 +182,24 @@ impl RadioGroup {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for Radio {
|
||||
fn from(label: &'static str) -> Self {
|
||||
Self::new(label).label(label)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SharedString> for Radio {
|
||||
fn from(label: SharedString) -> Self {
|
||||
Self::new(label.clone()).label(label)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Radio {
|
||||
fn from(label: String) -> Self {
|
||||
Self::new(SharedString::from(label.clone())).label(SharedString::from(label))
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for RadioGroup {
|
||||
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
||||
let on_change = self.on_change;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::{h_flex, ActiveTheme, Disableable, Side, Sizable, Size};
|
||||
use crate::{h_flex, text::Text, ActiveTheme, Disableable, Side, Sizable, Size};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, Animation, AnimationExt as _, AnyElement, App, Element,
|
||||
ElementId, GlobalElementId, InteractiveElement, IntoElement, LayoutId, ParentElement as _,
|
||||
SharedString, Styled as _, Window,
|
||||
Styled as _, Window,
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ pub struct Switch {
|
|||
id: ElementId,
|
||||
checked: bool,
|
||||
disabled: bool,
|
||||
label: Option<SharedString>,
|
||||
label: Option<Text>,
|
||||
label_side: Side,
|
||||
on_click: Option<Rc<dyn Fn(&bool, &mut Window, &mut App)>>,
|
||||
size: Size,
|
||||
|
|
@ -35,7 +35,7 @@ impl Switch {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
||||
pub fn label(mut self, label: impl Into<Text>) -> Self {
|
||||
self.label = Some(label.into());
|
||||
self
|
||||
}
|
||||
|
|
@ -193,7 +193,7 @@ impl Element for Switch {
|
|||
),
|
||||
),
|
||||
)
|
||||
.when_some(self.label.clone(), |this, label| {
|
||||
.when_some(self.label.take(), |this, label| {
|
||||
this.child(div().child(label).map(|this| match self.size {
|
||||
Size::XSmall | Size::Small => this.text_sm(),
|
||||
_ => this.text_base(),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use gpui::{
|
|||
|
||||
use crate::{h_flex, v_flex, ActiveTheme as _, Icon, IconName};
|
||||
|
||||
use super::utils::list_item_prefix;
|
||||
use super::{utils::list_item_prefix, TextViewStyle};
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Default, Clone, PartialEq)]
|
||||
|
|
@ -172,8 +172,10 @@ impl Paragraph {
|
|||
}
|
||||
}
|
||||
|
||||
/// Ref:
|
||||
/// https://ui.shadcn.com/docs/components/typography
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone, IntoElement, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Node {
|
||||
Root {
|
||||
children: Vec<Node>,
|
||||
|
|
@ -317,7 +319,7 @@ impl RenderOnce for Paragraph {
|
|||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ListState {
|
||||
pub(crate) struct ListState {
|
||||
todo: bool,
|
||||
ordered: bool,
|
||||
depth: usize,
|
||||
|
|
@ -328,6 +330,7 @@ impl Node {
|
|||
item: Node,
|
||||
ix: usize,
|
||||
state: ListState,
|
||||
text_view_style: &TextViewStyle,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> impl IntoElement {
|
||||
|
|
@ -373,24 +376,26 @@ impl Node {
|
|||
}),
|
||||
)
|
||||
})
|
||||
.child(child.render_node(
|
||||
.child(child.render(
|
||||
Some(ListState {
|
||||
depth: state.depth + 1,
|
||||
ordered: state.ordered,
|
||||
todo: checked.is_some(),
|
||||
}),
|
||||
text_view_style,
|
||||
window,
|
||||
cx,
|
||||
)),
|
||||
);
|
||||
}
|
||||
Node::List { .. } => {
|
||||
items.push(div().ml(rems(1.)).child(child.render_node(
|
||||
items.push(div().ml(rems(1.)).child(child.render(
|
||||
Some(ListState {
|
||||
depth: state.depth + 1,
|
||||
ordered: state.ordered,
|
||||
todo: checked.is_some(),
|
||||
}),
|
||||
text_view_style,
|
||||
window,
|
||||
cx,
|
||||
)))
|
||||
|
|
@ -477,17 +482,28 @@ impl Node {
|
|||
}
|
||||
}
|
||||
|
||||
fn render_node(
|
||||
pub(crate) fn render(
|
||||
self,
|
||||
list_state: Option<ListState>,
|
||||
text_view_style: &TextViewStyle,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> impl IntoElement {
|
||||
let in_list = list_state.is_some();
|
||||
let mb = if in_list { rems(0.0) } else { rems(1.) };
|
||||
let mb = if in_list {
|
||||
rems(0.)
|
||||
} else {
|
||||
text_view_style.paragraph_gap
|
||||
};
|
||||
|
||||
match self {
|
||||
Node::Root { children } => div().children(children).into_any_element(),
|
||||
Node::Root { children } => div()
|
||||
.children(
|
||||
children
|
||||
.into_iter()
|
||||
.map(|c| c.render(None, text_view_style, window, cx)),
|
||||
)
|
||||
.into_any_element(),
|
||||
Node::Paragraph(paragraph) => div().mb(mb).child(paragraph).into_any_element(),
|
||||
Node::Heading { level, children } => {
|
||||
let (text_size, font_weight) = match level {
|
||||
|
|
@ -535,6 +551,7 @@ impl Node {
|
|||
todo: list_state.todo,
|
||||
depth: list_state.depth,
|
||||
},
|
||||
text_view_style,
|
||||
window,
|
||||
cx,
|
||||
));
|
||||
|
|
@ -573,11 +590,3 @@ impl Node {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ref:
|
||||
/// https://ui.shadcn.com/docs/components/typography
|
||||
impl RenderOnce for Node {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
self.render_node(None, window, cx)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use crate::v_flex;
|
|||
use super::element::{
|
||||
self, ImageNode, InlineTextStyle, LinkMark, Paragraph, Table, TableRow, TextNode,
|
||||
};
|
||||
use super::TextViewStyle;
|
||||
|
||||
const BLOCK_ELEMENTS: [&str; 33] = [
|
||||
"html",
|
||||
|
|
@ -78,9 +79,11 @@ pub(super) fn parse_html(source: &str) -> Result<element::Node, SharedString> {
|
|||
Ok(node)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) struct HtmlElement {
|
||||
id: ElementId,
|
||||
text: SharedString,
|
||||
style: TextViewStyle,
|
||||
}
|
||||
|
||||
impl HtmlElement {
|
||||
|
|
@ -88,6 +91,7 @@ impl HtmlElement {
|
|||
Self {
|
||||
id: id.into(),
|
||||
text: raw.into(),
|
||||
style: TextViewStyle::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +100,12 @@ impl HtmlElement {
|
|||
self.text = raw.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set TextViewStyle.
|
||||
pub(crate) fn style(mut self, style: impl Into<TextViewStyle>) -> Self {
|
||||
self.style = style.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -150,7 +160,7 @@ impl Element for HtmlElement {
|
|||
|
||||
let mut el = div()
|
||||
.map(|this| match root {
|
||||
Ok(node) => this.child(node),
|
||||
Ok(node) => this.child(node.render(None, &self.style, window, cx)),
|
||||
Err(err) => this.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use crate::v_flex;
|
|||
use super::{
|
||||
element::{self, ImageNode, InlineTextStyle, LinkMark, Paragraph, Span, Table, TableRow},
|
||||
html::parse_html,
|
||||
TextViewStyle,
|
||||
};
|
||||
|
||||
/// Markdown GFM renderer
|
||||
|
|
@ -30,9 +31,11 @@ use super::{
|
|||
/// - As a markdown editor.
|
||||
/// - Add custom markdown syntax.
|
||||
/// - Complex styles cumstomization.
|
||||
#[derive(Clone)]
|
||||
pub(super) struct MarkdownElement {
|
||||
id: ElementId,
|
||||
text: SharedString,
|
||||
style: TextViewStyle,
|
||||
}
|
||||
|
||||
impl MarkdownElement {
|
||||
|
|
@ -40,6 +43,7 @@ impl MarkdownElement {
|
|||
Self {
|
||||
id: id.into(),
|
||||
text: raw.into(),
|
||||
style: TextViewStyle::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,6 +52,12 @@ impl MarkdownElement {
|
|||
self.text = raw.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set TextViewStyle.
|
||||
pub(crate) fn style(mut self, style: impl Into<TextViewStyle>) -> Self {
|
||||
self.style = style.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -106,7 +116,7 @@ impl Element for MarkdownElement {
|
|||
|
||||
let mut el = div()
|
||||
.map(|this| match root {
|
||||
Ok(node) => this.child(node),
|
||||
Ok(node) => this.child(node.render(None, &self.style, window, cx)),
|
||||
Err(err) => this.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use gpui::{App, ElementId, IntoElement, RenderOnce, SharedString, Window};
|
||||
use gpui::{rems, App, ElementId, IntoElement, Rems, RenderOnce, SharedString, Window};
|
||||
use html::HtmlElement;
|
||||
use markdown::MarkdownElement;
|
||||
|
||||
|
|
@ -7,9 +7,79 @@ mod html;
|
|||
mod markdown;
|
||||
mod utils;
|
||||
|
||||
#[derive(IntoElement, Clone)]
|
||||
pub enum Text {
|
||||
String(SharedString),
|
||||
TextView(TextView),
|
||||
}
|
||||
|
||||
impl From<SharedString> for Text {
|
||||
fn from(s: SharedString) -> Self {
|
||||
Self::String(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Text {
|
||||
fn from(s: &str) -> Self {
|
||||
Self::String(SharedString::from(s.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Text {
|
||||
fn from(s: String) -> Self {
|
||||
Self::String(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TextView> for Text {
|
||||
fn from(e: TextView) -> Self {
|
||||
Self::TextView(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Text {
|
||||
fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
|
||||
match self {
|
||||
Self::String(s) => s.into_any_element(),
|
||||
Self::TextView(e) => e.into_any_element(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TextViewStyle used to customize the style for [`TextView`].
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TextViewStyle {
|
||||
paragraph_gap: Rems,
|
||||
}
|
||||
|
||||
impl Default for TextViewStyle {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
paragraph_gap: rems(1.),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TextViewStyle {
|
||||
/// Default style for inline text.
|
||||
///
|
||||
/// This style has no paragraph gap.
|
||||
pub fn inline() -> Self {
|
||||
Self {
|
||||
paragraph_gap: rems(0.),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set paragraph gap, default is 1 rem.
|
||||
pub fn paragraph_gap(mut self, gap: Rems) -> Self {
|
||||
self.paragraph_gap = gap;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A text view that can render Markdown or HTML.
|
||||
#[allow(private_interfaces)]
|
||||
#[derive(IntoElement)]
|
||||
#[derive(IntoElement, Clone)]
|
||||
pub enum TextView {
|
||||
Markdown(MarkdownElement),
|
||||
Html(HtmlElement),
|
||||
|
|
@ -33,6 +103,19 @@ impl TextView {
|
|||
Self::Html(el) => Self::Html(el.text(raw)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set [`TextViewStyle`].
|
||||
pub fn style(self, style: TextViewStyle) -> Self {
|
||||
match self {
|
||||
Self::Markdown(el) => Self::Markdown(el.style(style)),
|
||||
Self::Html(el) => Self::Html(el.style(style)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set to use [`TextViewStyle::inline`].
|
||||
pub fn inline(self) -> Self {
|
||||
self.style(TextViewStyle::inline())
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for TextView {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, AnyElement, AnyView, App, AppContext, Context, IntoElement,
|
||||
ParentElement, Render, SharedString, Styled, Window,
|
||||
ParentElement, Render, Styled, Window,
|
||||
};
|
||||
|
||||
use crate::ActiveTheme;
|
||||
use crate::{text::Text, ActiveTheme};
|
||||
|
||||
pub struct Tooltip {
|
||||
text: SharedString,
|
||||
text: Text,
|
||||
element_builder: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyElement>>,
|
||||
}
|
||||
|
||||
impl Tooltip {
|
||||
pub fn new(text: impl Into<SharedString>, _: &mut Window, cx: &mut App) -> AnyView {
|
||||
pub fn new(text: impl Into<Text>, _: &mut Window, cx: &mut App) -> AnyView {
|
||||
cx.new(|_| Self {
|
||||
text: text.into(),
|
||||
element_builder: None,
|
||||
|
|
|
|||
Loading…
Reference in a new issue