409 lines · 12.4 KB · Rust
RawDownload
#![cfg_attr(target_family = "wasm", no_main)] use std::{    borrow::Cow,    ops::{Deref, DerefMut},    sync::Arc,}; use gpui::{    AbsoluteLength, App, Context, DefiniteLength, ElementId, Global, Hsla, Menu, SharedString,    TextStyle, TitlebarOptions, Window, WindowBounds, WindowOptions, bounds, colors::DefaultColors,    div, point, prelude::*, px, relative, rgb, size,};use gpui_platform::application;use std::iter; #[derive(Clone, Debug)]pub struct TextContext {    font_size: f32,    line_height: f32,    type_scale: f32,} impl Default for TextContext {    fn default() -> Self {        TextContext {            font_size: 16.0,            line_height: 1.3,            type_scale: 1.33,        }    }} impl TextContext {    pub fn get_global(cx: &App) -> &Arc<TextContext> {        &cx.global::<GlobalTextContext>().0    }} #[derive(Clone, Debug)]pub struct GlobalTextContext(pub Arc<TextContext>); impl Deref for GlobalTextContext {    type Target = Arc<TextContext>;     fn deref(&self) -> &Self::Target {        &self.0    }} impl DerefMut for GlobalTextContext {    fn deref_mut(&mut self) -> &mut Self::Target {        &mut self.0    }} impl Global for GlobalTextContext {} pub trait ActiveTextContext {    fn text_context(&self) -> &Arc<TextContext>;} impl ActiveTextContext for App {    fn text_context(&self) -> &Arc<TextContext> {        &self.global::<GlobalTextContext>().0    }} #[derive(Clone, PartialEq)]pub struct SpecimenTheme {    pub bg: Hsla,    pub fg: Hsla,} impl Default for SpecimenTheme {    fn default() -> Self {        Self {            bg: gpui::white(),            fg: gpui::black(),        }    }} impl SpecimenTheme {    pub fn invert(&self) -> Self {        Self {            bg: self.fg,            fg: self.bg,        }    }} #[derive(Debug, Clone, PartialEq, IntoElement)]struct Specimen {    id: ElementId,    scale: f32,    text_style: Option<TextStyle>,    string: SharedString,    invert: bool,} impl Specimen {    pub fn new(id: usize) -> Self {        let string = SharedString::new_static("The quick brown fox jumps over the lazy dog");        let id_string = format!("specimen-{}", id);        let id = ElementId::Name(id_string.into());        Self {            id,            scale: 1.0,            text_style: None,            string,            invert: false,        }    }     pub fn invert(mut self) -> Self {        self.invert = !self.invert;        self    }     pub fn scale(mut self, scale: f32) -> Self {        self.scale = scale;        self    }} impl RenderOnce for Specimen {    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {        let rem_size = window.rem_size();        let scale = self.scale;        let global_style = cx.text_context();         let style_override = self.text_style;         let mut font_size = global_style.font_size;        let mut line_height = global_style.line_height;         if let Some(style_override) = style_override {            font_size = style_override.font_size.to_pixels(rem_size).into();            line_height = match style_override.line_height {                DefiniteLength::Absolute(absolute_len) => match absolute_len {                    AbsoluteLength::Rems(absolute_len) => absolute_len.to_pixels(rem_size).into(),                    AbsoluteLength::Pixels(absolute_len) => absolute_len.into(),                },                DefiniteLength::Fraction(value) => value,            };        }         let mut theme = SpecimenTheme::default();         if self.invert {            theme = theme.invert();        }         div()            .id(self.id)            .bg(theme.bg)            .text_color(theme.fg)            .text_size(px(font_size * scale))            .line_height(relative(line_height))            .p(px(10.0))            .child(self.string)    }} #[derive(Debug, Clone, PartialEq, IntoElement)]struct CharacterGrid {    scale: f32,    invert: bool,    text_style: Option<TextStyle>,} impl CharacterGrid {    pub fn new() -> Self {        Self {            scale: 1.0,            invert: false,            text_style: None,        }    }     pub fn scale(mut self, scale: f32) -> Self {        self.scale = scale;        self    }} impl RenderOnce for CharacterGrid {    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {        let mut theme = SpecimenTheme::default();         if self.invert {            theme = theme.invert();        }         let characters = vec![            "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "A", "B", "C", "D", "E", "F", "G",            "H", "I", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",            "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "p", "q",            "r", "s", "t", "u", "v", "w", "x", "y", "z", "ẞ", "ſ", "ß", "ð", "Þ", "þ", "α", "β",            "Γ", "γ", "Δ", "δ", "η", "θ", "ι", "κ", "Λ", "λ", "μ", "ν", "ξ", "π", "τ", "υ", "φ",            "χ", "ψ", "∂", "а", "в", "Ж", "ж", "З", "з", "К", "к", "л", "м", "Н", "н", "Р", "р",            "У", "у", "ф", "ч", "ь", "ы", "Э", "э", "Я", "я", "ij", "öẋ", ".,", "⣝⣑", "~", "*",            "_", "^", "`", "'", "(", "{", "«", "#", "&", "@", "$", "¢", "%", "|", "?", "¶", "µ",            "❮", "<=", "!=", "==", "--", "++", "=>", "->", "🏀", "🎊", "😍", "❤️", "👍", "👎",        ];         let columns = 20;        let rows = characters.len().div_ceil(columns);         let grid_rows = (0..rows).map(|row_idx| {            let start_idx = row_idx * columns;            let end_idx = (start_idx + columns).min(characters.len());             div()                .w_full()                .flex()                .flex_row()                .children((start_idx..end_idx).map(|i| {                    div()                        .text_center()                        .size(px(62.))                        .bg(theme.bg)                        .text_color(theme.fg)                        .text_size(px(24.0))                        .line_height(relative(1.0))                        .child(characters[i])                }))                .when(end_idx - start_idx < columns, |d| {                    d.children(                        iter::repeat_with(|| div().flex_1()).take(columns - (end_idx - start_idx)),                    )                })        });         div().p_4().gap_2().flex().flex_col().children(grid_rows)    }} struct TextExample {    next_id: usize,    font_family: SharedString,} impl TextExample {    fn next_id(&mut self) -> usize {        self.next_id += 1;        self.next_id    }     fn button(        text: &str,        cx: &mut Context<Self>,        on_click: impl Fn(&mut Self, &mut Context<Self>) + 'static,    ) -> impl IntoElement {        div()            .id(text.to_string())            .flex_none()            .child(text.to_string())            .bg(gpui::black())            .text_color(gpui::white())            .active(|this| this.opacity(0.8))            .px_3()            .py_1()            .on_click(cx.listener(move |this, _, _, cx| on_click(this, cx)))    }} const FONT_FAMILIES: [&str; 5] = [    ".ZedMono",    ".SystemUIFont",    "Menlo",    "Monaco",    "Courier New",]; impl Render for TextExample {    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {        let tcx = cx.text_context();        let colors = cx.default_colors().clone();         let type_scale = tcx.type_scale;         let step_down_2 = 1.0 / (type_scale * type_scale);        let step_down_1 = 1.0 / type_scale;        let base = 1.0;        let step_up_1 = base * type_scale;        let step_up_2 = step_up_1 * type_scale;        let step_up_3 = step_up_2 * type_scale;        let step_up_4 = step_up_3 * type_scale;        let step_up_5 = step_up_4 * type_scale;        let step_up_6 = step_up_5 * type_scale;         div()            .font_family(self.font_family.clone())            .size_full()            .child(                div()                    .bg(gpui::white())                    .border_b_1()                    .border_color(gpui::black())                    .p_3()                    .flex()                    .child(Self::button(&self.font_family, cx, |this, cx| {                        let new_family = FONT_FAMILIES                            .iter()                            .position(|f| *f == this.font_family.as_str())                            .map(|idx| FONT_FAMILIES[(idx + 1) % FONT_FAMILIES.len()])                            .unwrap_or(FONT_FAMILIES[0]);                         this.font_family = SharedString::new(new_family);                        cx.notify();                    })),            )            .child(                div()                    .id("text-example")                    .overflow_y_scroll()                    .overflow_x_hidden()                    .bg(rgb(0xffffff))                    .size_full()                    .child(div().child(CharacterGrid::new().scale(base)))                    .child(                        div()                            .child(Specimen::new(self.next_id()).scale(step_down_2))                            .child(Specimen::new(self.next_id()).scale(step_down_2).invert())                            .child(Specimen::new(self.next_id()).scale(step_down_1))                            .child(Specimen::new(self.next_id()).scale(step_down_1).invert())                            .child(Specimen::new(self.next_id()).scale(base))                            .child(Specimen::new(self.next_id()).scale(base).invert())                            .child(Specimen::new(self.next_id()).scale(step_up_1))                            .child(Specimen::new(self.next_id()).scale(step_up_1).invert())                            .child(Specimen::new(self.next_id()).scale(step_up_2))                            .child(Specimen::new(self.next_id()).scale(step_up_2).invert())                            .child(Specimen::new(self.next_id()).scale(step_up_3))                            .child(Specimen::new(self.next_id()).scale(step_up_3).invert())                            .child(Specimen::new(self.next_id()).scale(step_up_4))                            .child(Specimen::new(self.next_id()).scale(step_up_4).invert())                            .child(Specimen::new(self.next_id()).scale(step_up_5))                            .child(Specimen::new(self.next_id()).scale(step_up_5).invert())                            .child(Specimen::new(self.next_id()).scale(step_up_6))                            .child(Specimen::new(self.next_id()).scale(step_up_6).invert()),                    ),            )            .child(div().w(px(240.)).h_full().bg(colors.container))    }} fn run_example() {    application().run(|cx: &mut App| {        cx.set_menus(vec![Menu {            name: "GPUI Typography".into(),            disabled: false,            items: vec![],        }]);         let fonts = [include_bytes!(            "../../../assets/fonts/lilex/Lilex-Regular.ttf"        )]        .iter()        .map(|b| Cow::Borrowed(&b[..]))        .collect();         _ = cx.text_system().add_fonts(fonts);         cx.init_colors();        cx.set_global(GlobalTextContext(Arc::new(TextContext::default())));         let window = cx            .open_window(                WindowOptions {                    titlebar: Some(TitlebarOptions {                        title: Some("GPUI Typography".into()),                        ..Default::default()                    }),                    window_bounds: Some(WindowBounds::Windowed(bounds(                        point(px(0.0), px(0.0)),                        size(px(920.), px(720.)),                    ))),                    ..Default::default()                },                |_window, cx| {                    cx.new(|_cx| TextExample {                        next_id: 0,                        font_family: ".ZedMono".into(),                    })                },            )            .unwrap();         window            .update(cx, |_view, _window, cx| {                cx.activate(true);            })            .unwrap();    });} #[cfg(not(target_family = "wasm"))]fn main() {    run_example();} #[cfg(target_family = "wasm")]#[wasm_bindgen::prelude::wasm_bindgen(start)]pub fn start() {    gpui_platform::web_init();    run_example();}