Skip to content

Commit

Permalink
Basic integration with new Piet text API
Browse files Browse the repository at this point in the history
This introduces a new type, TextLayout, that is intended to be
a component used by widgets that wish to display text, and which
exposes the functionality of the Piet text API.

This commit is a checkpoint; it adds only a small amount of new
functionality, and largely tries to keep things working as they
do currently.
  • Loading branch information
cmyr committed Sep 8, 2020
1 parent efad9ab commit bb5e6ef
Show file tree
Hide file tree
Showing 15 changed files with 531 additions and 270 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ You can find its changes [documented below](#060---2020-06-01).
- `Command::to` and `Command::target` to set and get a commands target. ([#1185] by [@finnerale])
- `Menu` commands can now choose a custom target. ([#1185] by [@finnerale])
- `Movement::StartOfDocument`, `Movement::EndOfDocument`. ([#1092] by [@sysint64])
- `TextLayout` type simplifies drawing text ([#1182] by [@cmyr])

### Changed

Expand Down Expand Up @@ -418,6 +419,7 @@ Last release without a changelog :(
[#1171]: https://github.com/linebender/druid/pull/1171
[#1172]: https://github.com/linebender/druid/pull/1172
[#1173]: https://github.com/linebender/druid/pull/1173
[#1182]: https://github.com/linebender/druid/pull/1185
[#1185]: https://github.com/linebender/druid/pull/1185
[#1092]: https://github.com/linebender/druid/pull/1092

Expand Down
4 changes: 2 additions & 2 deletions druid-shell/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ x11 = ["x11rb", "nix", "cairo-sys-rs"]
[dependencies]
# NOTE: When changing the piet or kurbo versions, ensure that
# the kurbo version included in piet is compatible with the kurbo version specified here.
piet-common = "0.2.0-pre2"
piet-common = "0.2.0-pre3"
kurbo = "0.6.3"

log = "0.4.11"
Expand Down Expand Up @@ -79,5 +79,5 @@ version = "0.3.44"
features = ["Window", "MouseEvent", "CssStyleDeclaration", "WheelEvent", "KeyEvent", "KeyboardEvent"]

[dev-dependencies]
piet-common = { version = "0.2.0-pre2", features = ["png"] }
piet-common = { version = "0.2.0-pre3", features = ["png"] }
simple_logger = { version = "1.9.0", default-features = false }
26 changes: 13 additions & 13 deletions druid/examples/custom_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
//! An example of a custom drawing widget.

use druid::kurbo::BezPath;
use druid::piet::{FontFamily, ImageFormat, InterpolationMode, Text, TextLayoutBuilder};
use druid::piet::{FontFamily, ImageFormat, InterpolationMode};
use druid::widget::prelude::*;
use druid::{Affine, AppLauncher, Color, LocalizedString, Point, Rect, WindowDesc};
use druid::{
Affine, AppLauncher, Color, FontDescriptor, LocalizedString, Point, Rect, TextLayout,
WindowDesc,
};

struct CustomWidget;

Expand Down Expand Up @@ -54,7 +57,7 @@ impl Widget<String> for CustomWidget {
// The paint method gets called last, after an event flow.
// It goes event -> update -> layout -> paint, and each method can influence the next.
// Basically, anything that changes the appearance of a widget causes a paint.
fn paint(&mut self, ctx: &mut PaintCtx, data: &String, _env: &Env) {
fn paint(&mut self, ctx: &mut PaintCtx, data: &String, env: &Env) {
// Let's draw a picture with Piet!

// Clear the whole widget with the color of your choice
Expand All @@ -81,21 +84,18 @@ impl Widget<String> for CustomWidget {
let fill_color = Color::rgba8(0x00, 0x00, 0x00, 0x7F);
ctx.fill(rect, &fill_color);

// Text is easy, if you ignore all these unwraps. Just pick a font and a size.
// Here's where we actually use the UI state
let layout = ctx
.text()
.new_text_layout(data.as_str())
.font(FontFamily::SYSTEM_UI, 24.0)
.text_color(fill_color)
.build()
.unwrap();
// Text is easy; in real use TextLayout should be stored in the widget
// and reused.
let mut layout = TextLayout::new(data.as_str());
layout.set_font(FontDescriptor::new(FontFamily::SERIF).with_size(24.0));
layout.set_text_color(fill_color);
layout.rebuild_if_needed(&mut ctx.text(), env);

// Let's rotate our text slightly. First we save our current (default) context:
ctx.with_save(|ctx| {
// Now we can rotate the context (or set a clip path, for instance):
ctx.transform(Affine::rotate(0.1));
ctx.draw_text(&layout, (80.0, 40.0));
layout.draw(ctx, (80.0, 40.0));
});
// When we exit with_save, the original context's rotation is restored

Expand Down
27 changes: 15 additions & 12 deletions druid/examples/styled_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@

use druid::widget::{Checkbox, Flex, Label, MainAxisAlignment, Painter, Parse, Stepper, TextBox};
use druid::{
theme, AppLauncher, ArcStr, Color, Data, Key, Lens, LensExt, LensWrap, LocalizedString,
PlatformError, RenderContext, Widget, WidgetExt, WindowDesc,
theme, AppLauncher, Color, Data, FontDescriptor, FontFamily, Key, Lens, LensExt, LensWrap,
LocalizedString, PlatformError, RenderContext, Widget, WidgetExt, WindowDesc,
};
use std::fmt::Display;

// This is a custom key we'll use with Env to set and get our text size.
const MY_CUSTOM_TEXT_SIZE: Key<f64> = Key::new("styled_text.custom_text_size");
const MY_CUSTOM_FONT: Key<ArcStr> = Key::new("styled_text.custom_font");
// This is a custom key we'll use with Env to set and get our font.
const MY_CUSTOM_FONT: Key<FontDescriptor> = Key::new("styled_text.custom_font");

#[derive(Clone, Lens, Data)]
struct AppData {
Expand Down Expand Up @@ -73,7 +72,7 @@ fn ui_builder() -> impl Widget<AppData> {
});

// This is druid's default text style.
// It's set by theme::LABEL_COLOR, theme::TEXT_SIZE_NORMAL, and theme::FONT_NAME
// It's set by theme::LABEL_COLOR and theme::UI_FONT
let label =
Label::new(|data: &String, _env: &_| format!("Default: {}", data)).lens(AppData::text);

Expand All @@ -88,19 +87,19 @@ fn ui_builder() -> impl Widget<AppData> {
// to the default font)
let styled_label = Label::new(|data: &AppData, _env: &_| format!("{}", data))
.with_text_color(theme::PRIMARY_LIGHT)
.with_text_size(MY_CUSTOM_TEXT_SIZE)
.with_font(MY_CUSTOM_FONT)
.background(my_painter)
.on_click(|_, data, _| {
data.size *= 1.1;
})
.env_scope(|env: &mut druid::Env, data: &AppData| {
env.set(MY_CUSTOM_TEXT_SIZE, data.size);
if data.mono {
env.set(MY_CUSTOM_FONT, "monospace");
let new_font = if data.mono {
FontDescriptor::new(FontFamily::MONOSPACE)
} else {
env.set(MY_CUSTOM_FONT, env.get(theme::FONT_NAME));
FontDescriptor::new(FontFamily::SYSTEM_UI)
}
.with_size(data.size);
env.set(MY_CUSTOM_FONT, new_font);
});

let stepper = Stepper::new()
Expand All @@ -118,7 +117,10 @@ fn ui_builder() -> impl Widget<AppData> {

let mono_checkbox = Checkbox::new("Monospace").lens(AppData::mono);

let input = TextBox::new().fix_width(200.0).lens(AppData::text);
let input = TextBox::new()
.with_text_size(38.0)
.fix_width(200.0)
.lens(AppData::text);

Flex::column()
.main_axis_alignment(MainAxisAlignment::Center)
Expand All @@ -131,4 +133,5 @@ fn ui_builder() -> impl Widget<AppData> {
.with_child(mono_checkbox)
.with_spacer(8.0)
.with_child(input.padding(5.0))
.debug_widget_id()
}
50 changes: 22 additions & 28 deletions druid/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@ use std::collections::{HashMap, VecDeque};
use crate::bloom::Bloom;
use crate::contexts::ContextState;
use crate::kurbo::{Affine, Insets, Point, Rect, Shape, Size, Vec2};
use crate::piet::{FontFamily, PietTextLayout, RenderContext, Text, TextLayout, TextLayoutBuilder};
use crate::util::ExtendDrain;
use crate::{
BoxConstraints, Color, Command, Data, Env, Event, EventCtx, InternalEvent, InternalLifeCycle,
LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx, Region, Target, TimerToken, UpdateCtx, Widget,
WidgetId,
LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx, Region, RenderContext, Target, TextLayout,
TimerToken, UpdateCtx, Widget, WidgetId,
};

/// Our queue type
Expand All @@ -51,7 +50,7 @@ pub struct WidgetPod<T, W> {
env: Option<Env>,
inner: W,
// stashed layout so we don't recompute this when debugging
debug_widget_text: Option<PietTextLayout>,
debug_widget_text: TextLayout,
}

/// Generic state for all widgets in the hierarchy.
Expand Down Expand Up @@ -145,7 +144,7 @@ impl<T, W: Widget<T>> WidgetPod<T, W> {
old_data: None,
env: None,
inner,
debug_widget_text: None,
debug_widget_text: TextLayout::new(""),
}
}

Expand Down Expand Up @@ -429,7 +428,7 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
}

fn make_widget_id_layout_if_needed(&mut self, id: WidgetId, ctx: &mut PaintCtx, env: &Env) {
if self.debug_widget_text.is_none() {
if self.debug_widget_text.needs_rebuild() {
// switch text color based on background, this is meh and that's okay
let border_color = env.get_debug_color(id.to_raw());
let (r, g, b, _) = border_color.as_rgba8();
Expand All @@ -439,34 +438,29 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
} else {
Color::BLACK
};

let id_string = id.to_raw().to_string();
self.debug_widget_text = ctx
.text()
.new_text_layout(id_string)
.font(FontFamily::SYSTEM_UI, 10.0)
.text_color(text_color)
.build()
.ok();
self.debug_widget_text.set_text(id_string);
self.debug_widget_text.set_text_size(10.0);
self.debug_widget_text.set_text_color(text_color);
self.debug_widget_text
.rebuild_if_needed(&mut ctx.text(), env);
}
}

fn debug_paint_widget_ids(&self, ctx: &mut PaintCtx, env: &Env) {
// we clone because we need to move it for paint_with_z_index
let text = self.debug_widget_text.clone();
if let Some(text) = text {
let text_size = text.size();
let origin = ctx.size().to_vec2() - text_size.to_vec2();
let border_color = env.get_debug_color(ctx.widget_id().to_raw());
self.debug_paint_layout_bounds(ctx, env);

ctx.paint_with_z_index(ctx.depth(), move |ctx| {
let origin = Point::new(origin.x.max(0.0), origin.y.max(0.0));
let text_rect = Rect::from_origin_size(origin, text_size);
ctx.fill(text_rect, &border_color);
ctx.draw_text(&text, origin);
})
}
let text_size = text.size();
let origin = ctx.size().to_vec2() - text_size.to_vec2();
let border_color = env.get_debug_color(ctx.widget_id().to_raw());
self.debug_paint_layout_bounds(ctx, env);

ctx.paint_with_z_index(ctx.depth(), move |ctx| {
let origin = Point::new(origin.x.max(0.0), origin.y.max(0.0));
let text_rect = Rect::from_origin_size(origin, text_size);
ctx.fill(text_rect, &border_color);
text.draw(ctx, origin);
})
}

fn debug_paint_layout_bounds(&self, ctx: &mut PaintCtx, env: &Env) {
Expand Down Expand Up @@ -1008,7 +1002,7 @@ mod tests {
state: &mut state,
};

let env = Env::default();
let env = crate::theme::init();

widget.lifecycle(&mut ctx, &LifeCycle::WidgetAdded, &None, &env);
assert!(ctx.widget_state.children.may_contain(&ID_1));
Expand Down
4 changes: 2 additions & 2 deletions druid/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ struct EnvImpl {
///
/// [`ValueType`]: trait.ValueType.html
/// [`Env`]: struct.Env.html
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Key<T> {
key: &'static str,
value_type: PhantomData<*const T>,
Expand Down Expand Up @@ -120,7 +120,7 @@ pub enum Value {
///
/// [`Key<T>`]: struct.Key.html
/// [`Env`]: struct.Env.html
#[derive(Clone)]
#[derive(Clone, Debug)]
pub enum KeyOrValue<T> {
/// A concrete [`Value`] of type `T`.
///
Expand Down
2 changes: 1 addition & 1 deletion druid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ pub use lens::{Lens, LensExt, LensWrap};
pub use localization::LocalizedString;
pub use menu::{sys as platform_menus, ContextMenu, MenuDesc, MenuItem};
pub use mouse::MouseEvent;
pub use text::FontDescriptor;
pub use text::{FontDescriptor, TextLayout};
pub use widget::{Widget, WidgetExt, WidgetId};
pub use win_handler::DruidHandler;
pub use window::{Window, WindowId};
Expand Down
Loading

0 comments on commit bb5e6ef

Please sign in to comment.