styled_text/
styled_text.rs1#![windows_subsystem = "windows"]
19
20#[allow(deprecated)]
21use druid::widget::Parse;
22use druid::widget::{
23 Checkbox, CrossAxisAlignment, Flex, Label, LensWrap, MainAxisAlignment, Painter, Scroll,
24 Stepper, TextBox,
25};
26use druid::{
27 theme, AppLauncher, Color, Data, FontDescriptor, FontFamily, Key, Lens, LensExt,
28 LocalizedString, PlatformError, RenderContext, Widget, WidgetExt, WindowDesc,
29};
30use std::fmt::Display;
31
32const MY_CUSTOM_FONT: Key<FontDescriptor> = Key::new("org.linebender.example.my-custom-font");
34
35const COLUMN_WIDTH: f64 = 360.0;
36
37#[derive(Clone, Lens, Data)]
38struct AppData {
39 text: String,
40 size: f64,
41 mono: bool,
42}
43impl Display for AppData {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 write!(
46 f,
47 "Size {:.1}{}: {}",
48 self.size,
49 if self.mono { " mono" } else { "" },
50 self.text
51 )
52 }
53}
54
55pub fn main() -> Result<(), PlatformError> {
56 let main_window = WindowDesc::new(ui_builder()).title(
57 LocalizedString::new("styled-text-demo-window-title").with_placeholder("Type Styler"),
58 );
59 let data = AppData {
60 text: "Here's some sample text".to_string(),
61 size: 24.0,
62 mono: false,
63 };
64
65 AppLauncher::with_window(main_window)
66 .log_to_console()
67 .launch(data)?;
68
69 Ok(())
70}
71
72fn ui_builder() -> impl Widget<AppData> {
73 let my_painter = Painter::new(|ctx, _, _| {
74 let bounds = ctx.size().to_rect();
75 if ctx.is_hot() {
76 ctx.fill(bounds, &Color::rgba8(0, 0, 0, 128));
77 }
78
79 if ctx.is_active() {
80 ctx.stroke(bounds, &Color::WHITE, 2.0);
81 }
82 });
83
84 let label =
87 Label::new(|data: &String, _env: &_| format!("Default: {data}")).lens(AppData::text);
88
89 let styled_label = Label::new(|data: &AppData, _env: &_| format!("{data}"))
99 .with_text_color(theme::PRIMARY_LIGHT)
100 .with_font(MY_CUSTOM_FONT)
101 .background(my_painter)
102 .on_click(|_, data, _| {
103 data.size *= 1.1;
104 })
105 .env_scope(|env: &mut druid::Env, data: &AppData| {
106 let new_font = if data.mono {
107 FontDescriptor::new(FontFamily::MONOSPACE)
108 } else {
109 FontDescriptor::new(FontFamily::SYSTEM_UI)
110 }
111 .with_size(data.size);
112 env.set(MY_CUSTOM_FONT, new_font);
113 });
114
115 let labels = Scroll::new(
116 Flex::column()
117 .cross_axis_alignment(CrossAxisAlignment::Start)
118 .with_child(label)
119 .with_default_spacer()
120 .with_child(styled_label),
121 )
122 .expand_height()
123 .fix_width(COLUMN_WIDTH);
124
125 let stepper = Stepper::new()
126 .with_range(0.0, 100.0)
127 .with_step(1.0)
128 .with_wraparound(false)
129 .lens(AppData::size);
130
131 #[allow(deprecated)]
133 let stepper_textbox = LensWrap::new(
134 Parse::new(TextBox::new()),
135 AppData::size.map(|x| Some(*x), |x, y| *x = y.unwrap_or(24.0)),
136 );
137
138 let mono_checkbox = Checkbox::new("Monospace").lens(AppData::mono);
139 let stepper_row = Flex::row()
140 .with_child(stepper_textbox)
141 .with_child(stepper)
142 .with_default_spacer()
143 .with_child(mono_checkbox);
144
145 let input = TextBox::multiline()
146 .with_placeholder("Your sample text here :)")
147 .fix_width(COLUMN_WIDTH)
148 .fix_height(140.0)
149 .lens(AppData::text);
150
151 Flex::column()
152 .main_axis_alignment(MainAxisAlignment::Center)
153 .with_default_spacer()
154 .with_flex_child(labels, 1.0)
155 .with_default_spacer()
156 .with_child(input)
157 .with_default_spacer()
158 .with_child(stepper_row)
159 .with_default_spacer()
160}