584 lines
19 KiB
Rust
584 lines
19 KiB
Rust
//! Text Example
|
||
//!
|
||
//! This example demonstrates text capabilities in GPUI:
|
||
//!
|
||
//! 1. Text Styling - Font sizes, weights, and colors
|
||
//! 2. Text Alignment - Left, center, right alignment
|
||
//! 3. Text Decoration - Underline, strikethrough
|
||
//! 4. Text Overflow - Ellipsis, truncation, line clamping
|
||
//! 5. Styled Text - Inline style variations with highlights
|
||
//! 6. Character Grid - Unicode and emoji support
|
||
|
||
#[path = "../prelude.rs"]
|
||
mod example_prelude;
|
||
|
||
use example_prelude::init_example;
|
||
use gpui::{
|
||
App, Application, Bounds, Context, FontStyle, FontWeight, Hsla, Render, StyledText,
|
||
TextOverflow, Window, WindowBounds, WindowOptions, colors::Colors, current_platform, div,
|
||
prelude::*, px, relative, rgb, size,
|
||
};
|
||
|
||
// Text Styling Examples
|
||
|
||
fn text_sizes_example(colors: &Colors) -> impl IntoElement {
|
||
let text = colors.text;
|
||
let text_muted = colors.disabled;
|
||
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_1()
|
||
.child(
|
||
div()
|
||
.text_xs()
|
||
.text_color(text_muted)
|
||
.child("Font sizes: text_xs, text_sm, text_base, text_lg, text_xl"),
|
||
)
|
||
.child(
|
||
div()
|
||
.flex()
|
||
.flex_wrap()
|
||
.items_baseline()
|
||
.gap_3()
|
||
.child(div().text_xs().text_color(text).child("Extra Small"))
|
||
.child(div().text_sm().text_color(text).child("Small"))
|
||
.child(div().text_base().text_color(text).child("Base"))
|
||
.child(div().text_lg().text_color(text).child("Large"))
|
||
.child(div().text_xl().text_color(text).child("Extra Large")),
|
||
)
|
||
}
|
||
|
||
fn text_weights_example(colors: &Colors) -> impl IntoElement {
|
||
let text = colors.text;
|
||
let text_muted = colors.disabled;
|
||
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_1()
|
||
.child(
|
||
div()
|
||
.text_xs()
|
||
.text_color(text_muted)
|
||
.child("Font weights: THIN through BLACK"),
|
||
)
|
||
.child(
|
||
div()
|
||
.flex()
|
||
.flex_wrap()
|
||
.gap_3()
|
||
.child(
|
||
div()
|
||
.text_color(text)
|
||
.font_weight(FontWeight::THIN)
|
||
.child("Thin"),
|
||
)
|
||
.child(
|
||
div()
|
||
.text_color(text)
|
||
.font_weight(FontWeight::LIGHT)
|
||
.child("Light"),
|
||
)
|
||
.child(
|
||
div()
|
||
.text_color(text)
|
||
.font_weight(FontWeight::NORMAL)
|
||
.child("Normal"),
|
||
)
|
||
.child(
|
||
div()
|
||
.text_color(text)
|
||
.font_weight(FontWeight::MEDIUM)
|
||
.child("Medium"),
|
||
)
|
||
.child(
|
||
div()
|
||
.text_color(text)
|
||
.font_weight(FontWeight::SEMIBOLD)
|
||
.child("Semibold"),
|
||
)
|
||
.child(
|
||
div()
|
||
.text_color(text)
|
||
.font_weight(FontWeight::BOLD)
|
||
.child("Bold"),
|
||
)
|
||
.child(
|
||
div()
|
||
.text_color(text)
|
||
.font_weight(FontWeight::BLACK)
|
||
.child("Black"),
|
||
),
|
||
)
|
||
}
|
||
|
||
// Text Alignment Examples
|
||
|
||
fn text_alignment_example(colors: &Colors) -> impl IntoElement {
|
||
let text = colors.text;
|
||
let text_muted = colors.disabled;
|
||
let surface = colors.container;
|
||
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_2()
|
||
.child(
|
||
div()
|
||
.text_xs()
|
||
.text_color(text_muted)
|
||
.child("Alignment: default (left), text_center, text_right"),
|
||
)
|
||
.child(
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_1()
|
||
.child(
|
||
div()
|
||
.p_2()
|
||
.bg(surface)
|
||
.rounded_sm()
|
||
.text_color(text)
|
||
.child("Left aligned (default)"),
|
||
)
|
||
.child(
|
||
div()
|
||
.p_2()
|
||
.bg(surface)
|
||
.rounded_sm()
|
||
.text_center()
|
||
.text_color(text)
|
||
.child("Center aligned"),
|
||
)
|
||
.child(
|
||
div()
|
||
.p_2()
|
||
.bg(surface)
|
||
.rounded_sm()
|
||
.text_right()
|
||
.text_color(text)
|
||
.child("Right aligned"),
|
||
),
|
||
)
|
||
}
|
||
|
||
// Text Decoration Examples
|
||
|
||
fn text_decoration_example(colors: &Colors) -> impl IntoElement {
|
||
let text = colors.text;
|
||
let text_muted = colors.disabled;
|
||
let accent = colors.selected;
|
||
let error = rgb(0xd32f2f);
|
||
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_2()
|
||
.child(
|
||
div()
|
||
.text_xs()
|
||
.text_color(text_muted)
|
||
.child("Decorations: underline, strikethrough, italic"),
|
||
)
|
||
.child(
|
||
div()
|
||
.flex()
|
||
.flex_wrap()
|
||
.gap_4()
|
||
.child(
|
||
div()
|
||
.text_color(text)
|
||
.text_decoration_1()
|
||
.text_decoration_color(accent)
|
||
.child("Underlined text"),
|
||
)
|
||
.child(
|
||
div()
|
||
.text_color(text)
|
||
.line_through()
|
||
.text_decoration_color(error)
|
||
.child("Strikethrough text"),
|
||
)
|
||
.child(div().text_color(text).italic().child("Italic text")),
|
||
)
|
||
}
|
||
|
||
// Text Overflow Examples
|
||
|
||
fn text_overflow_example(colors: &Colors) -> impl IntoElement {
|
||
let text = colors.text;
|
||
let text_muted = colors.disabled;
|
||
let surface = colors.container;
|
||
let border = colors.border;
|
||
|
||
let long_text = "The quick brown fox jumps over the lazy dog. This is a long sentence that will overflow its container.";
|
||
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_2()
|
||
.child(
|
||
div()
|
||
.text_xs()
|
||
.text_color(text_muted)
|
||
.child("Overflow handling: ellipsis, truncate, line_clamp"),
|
||
)
|
||
.child(
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_2()
|
||
.child(
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_1()
|
||
.child(
|
||
div()
|
||
.text_xs()
|
||
.text_color(text_muted)
|
||
.child("text_ellipsis (single line):"),
|
||
)
|
||
.child(
|
||
div()
|
||
.p_2()
|
||
.bg(surface)
|
||
.border_1()
|
||
.border_color(border)
|
||
.rounded_sm()
|
||
.text_color(text)
|
||
.overflow_hidden()
|
||
.text_ellipsis()
|
||
.child(long_text),
|
||
),
|
||
)
|
||
.child(
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_1()
|
||
.child(
|
||
div()
|
||
.text_xs()
|
||
.text_color(text_muted)
|
||
.child("line_clamp(2):"),
|
||
)
|
||
.child(
|
||
div()
|
||
.p_2()
|
||
.bg(surface)
|
||
.border_1()
|
||
.border_color(border)
|
||
.rounded_sm()
|
||
.text_color(text)
|
||
.overflow_hidden()
|
||
.text_ellipsis()
|
||
.line_clamp(2)
|
||
.child(long_text),
|
||
),
|
||
)
|
||
.child(
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_1()
|
||
.child(
|
||
div()
|
||
.text_xs()
|
||
.text_color(text_muted)
|
||
.child("truncate (hard cut):"),
|
||
)
|
||
.child(
|
||
div()
|
||
.p_2()
|
||
.bg(surface)
|
||
.border_1()
|
||
.border_color(border)
|
||
.rounded_sm()
|
||
.text_color(text)
|
||
.overflow_hidden()
|
||
.text_overflow(TextOverflow::Truncate("".into()))
|
||
.child(long_text),
|
||
),
|
||
)
|
||
.child(
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_1()
|
||
.child(
|
||
div()
|
||
.text_xs()
|
||
.text_color(text_muted)
|
||
.child("whitespace_nowrap:"),
|
||
)
|
||
.child(
|
||
div()
|
||
.p_2()
|
||
.bg(surface)
|
||
.border_1()
|
||
.border_color(border)
|
||
.rounded_sm()
|
||
.text_color(text)
|
||
.overflow_hidden()
|
||
.whitespace_nowrap()
|
||
.child(long_text),
|
||
),
|
||
),
|
||
)
|
||
}
|
||
|
||
// Styled Text Examples
|
||
|
||
fn styled_text_example(colors: &Colors) -> impl IntoElement {
|
||
let text = colors.text;
|
||
let text_muted = colors.disabled;
|
||
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_2()
|
||
.child(
|
||
div()
|
||
.text_xs()
|
||
.text_color(text_muted)
|
||
.child("StyledText with inline highlights"),
|
||
)
|
||
.child(div().text_lg().text_color(text).child(
|
||
StyledText::new("Bold Italic Normal Semibold").with_highlights([
|
||
(0..4, FontWeight::BOLD.into()),
|
||
(5..11, FontStyle::Italic.into()),
|
||
(19..27, FontWeight::SEMIBOLD.into()),
|
||
]),
|
||
))
|
||
}
|
||
|
||
// Character Grid Example
|
||
|
||
fn character_grid_example(colors: &Colors) -> impl IntoElement {
|
||
let text = colors.text;
|
||
let text_muted = colors.disabled;
|
||
let surface = colors.container;
|
||
let border = colors.border;
|
||
|
||
let characters = [
|
||
// Latin
|
||
"A", "B", "C", "D", "E", "a", "b", "c", "d", "e", // Numbers
|
||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", // Greek
|
||
"α", "β", "γ", "δ", "ε", "θ", "λ", "π", "σ", "ω", // Cyrillic
|
||
"Д", "Ж", "И", "Л", "Ф", "Ц", "Ш", "Щ", "Ы", "Я", // CJK
|
||
"你", "好", "世", "界", "日", "本", "語", "中", "文", "字", // Symbols
|
||
"→", "←", "↑", "↓", "•", "★", "♠", "♥", "♦", "♣", // Emoji
|
||
"😀", "🎉", "🚀", "💡", "🔥", "✨", "🎨", "📚", "🎵", "❤️",
|
||
];
|
||
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_2()
|
||
.child(
|
||
div()
|
||
.text_xs()
|
||
.text_color(text_muted)
|
||
.child("Unicode and emoji support"),
|
||
)
|
||
.child(
|
||
div()
|
||
.p_2()
|
||
.bg(surface)
|
||
.border_1()
|
||
.border_color(border)
|
||
.rounded_md()
|
||
.child(
|
||
div()
|
||
.grid()
|
||
.grid_cols(10)
|
||
.gap_1()
|
||
.children(characters.iter().map(|c| {
|
||
div()
|
||
.flex()
|
||
.items_center()
|
||
.justify_center()
|
||
.size_8()
|
||
.text_lg()
|
||
.text_color(text)
|
||
.line_height(relative(1.0))
|
||
.child(*c)
|
||
})),
|
||
),
|
||
)
|
||
}
|
||
|
||
// Line Height Example
|
||
|
||
fn line_height_example(colors: &Colors) -> impl IntoElement {
|
||
let text = colors.text;
|
||
let text_muted = colors.disabled;
|
||
let surface = colors.container;
|
||
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_2()
|
||
.child(
|
||
div()
|
||
.text_xs()
|
||
.text_color(text_muted)
|
||
.child("Line height: relative(1.0), relative(1.5), relative(2.0)"),
|
||
)
|
||
.child(
|
||
div()
|
||
.flex()
|
||
.gap_3()
|
||
.child(
|
||
div()
|
||
.flex_1()
|
||
.p_2()
|
||
.bg(surface)
|
||
.rounded_sm()
|
||
.text_color(text)
|
||
.text_sm()
|
||
.line_height(relative(1.0))
|
||
.child("Tight\nline\nheight"),
|
||
)
|
||
.child(
|
||
div()
|
||
.flex_1()
|
||
.p_2()
|
||
.bg(surface)
|
||
.rounded_sm()
|
||
.text_color(text)
|
||
.text_sm()
|
||
.line_height(relative(1.5))
|
||
.child("Normal\nline\nheight"),
|
||
)
|
||
.child(
|
||
div()
|
||
.flex_1()
|
||
.p_2()
|
||
.bg(surface)
|
||
.rounded_sm()
|
||
.text_color(text)
|
||
.text_sm()
|
||
.line_height(relative(2.0))
|
||
.child("Loose\nline\nheight"),
|
||
),
|
||
)
|
||
}
|
||
|
||
// Main Application View
|
||
|
||
struct TextExample;
|
||
|
||
impl Render for TextExample {
|
||
fn render(&mut self, window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||
let colors = Colors::for_appearance(window);
|
||
|
||
div()
|
||
.id("main")
|
||
.size_full()
|
||
.p_4()
|
||
.bg(colors.background)
|
||
.overflow_scroll()
|
||
.child(
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_4()
|
||
.max_w(px(600.))
|
||
.child(
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_1()
|
||
.child(
|
||
div()
|
||
.text_xl()
|
||
.font_weight(FontWeight::BOLD)
|
||
.text_color(colors.text)
|
||
.child("Text & Typography"),
|
||
)
|
||
.child(
|
||
div().text_sm().text_color(colors.disabled).child(
|
||
"Font styling, alignment, overflow, and unicode support",
|
||
),
|
||
),
|
||
)
|
||
.child(section(&colors, "Font Sizes", text_sizes_example(&colors)))
|
||
.child(section(
|
||
&colors,
|
||
"Font Weights",
|
||
text_weights_example(&colors),
|
||
))
|
||
.child(section(
|
||
&colors,
|
||
"Text Alignment",
|
||
text_alignment_example(&colors),
|
||
))
|
||
.child(section(
|
||
&colors,
|
||
"Text Decoration",
|
||
text_decoration_example(&colors),
|
||
))
|
||
.child(section(
|
||
&colors,
|
||
"Line Height",
|
||
line_height_example(&colors),
|
||
))
|
||
.child(section(
|
||
&colors,
|
||
"Styled Text",
|
||
styled_text_example(&colors),
|
||
))
|
||
.child(section(
|
||
&colors,
|
||
"Text Overflow",
|
||
text_overflow_example(&colors),
|
||
))
|
||
.child(section(
|
||
&colors,
|
||
"Character Grid",
|
||
character_grid_example(&colors),
|
||
)),
|
||
)
|
||
}
|
||
}
|
||
|
||
fn section(colors: &Colors, title: &'static str, content: impl IntoElement) -> impl IntoElement {
|
||
let surface: Hsla = colors.container.into();
|
||
|
||
div()
|
||
.flex()
|
||
.flex_col()
|
||
.gap_2()
|
||
.p_3()
|
||
.bg(surface.opacity(0.5))
|
||
.rounded_lg()
|
||
.child(
|
||
div()
|
||
.text_sm()
|
||
.font_weight(FontWeight::SEMIBOLD)
|
||
.text_color(colors.text)
|
||
.child(title),
|
||
)
|
||
.child(content)
|
||
}
|
||
|
||
fn main() {
|
||
Application::with_platform(current_platform(false)).run(|cx: &mut App| {
|
||
let bounds = Bounds::centered(None, size(px(650.), px(900.)), cx);
|
||
cx.open_window(
|
||
WindowOptions {
|
||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||
..Default::default()
|
||
},
|
||
|_, cx| cx.new(|_| TextExample),
|
||
)
|
||
.expect("Failed to open window");
|
||
|
||
init_example(cx, "Text");
|
||
});
|
||
}
|