blob: 4308749ad0a4e67711628948e71824a227db67e3 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::Error;
use carnelian::{
color::Color,
drawing::{load_font, FontFace},
facet::*,
input::{self},
render::Context as RenderContext,
App, AppAssistant, AppAssistantPtr, AppContext, AssistantCreatorFunc, Coord, LocalBoxFuture,
Size, ViewAssistant, ViewAssistantContext, ViewAssistantPtr, ViewKey,
};
use euclid::point2;
use fuchsia_zircon::Event;
use lipsum::lipsum_words;
use std::path::PathBuf;
const LINE_THICKNESS: Coord = 2.0;
const BASELINE_INDENT: Coord = 0.15;
const PADDING: Coord = 0.005;
struct FontAppAssistant {
app_context: AppContext,
}
impl AppAssistant for FontAppAssistant {
fn setup(&mut self) -> Result<(), Error> {
Ok(())
}
fn create_view_assistant(&mut self, view_key: ViewKey) -> Result<ViewAssistantPtr, Error> {
Ok(FontMetricsViewAssistant::new(&self.app_context, view_key)?)
}
}
struct SceneDetails {
scene: Scene,
sample_title: FacetId,
sample_paragraph: FacetId,
lines: Vec<FacetId>,
}
struct FontMetricsViewAssistant {
app_context: AppContext,
view_key: ViewKey,
sample_title: String,
sample_paragraph: String,
sample_faces: Vec<FontFace>,
sample_index: usize,
sample_size_divisor: f32,
label_face: FontFace,
scene_details: Option<SceneDetails>,
line_color: Color,
round_scene_corners: bool,
}
impl FontMetricsViewAssistant {
fn new(app_context: &AppContext, view_key: ViewKey) -> Result<ViewAssistantPtr, Error> {
let ss = load_font(PathBuf::from("/pkg/data/fonts/ShortStack-Regular.ttf"))?;
let ds = load_font(PathBuf::from("/pkg/data/fonts/DroidSerif-Regular.ttf"))?;
let qr = load_font(PathBuf::from("/pkg/data/fonts/Quintessential-Regular.ttf"))?;
let sample_faces = vec![ss, ds, qr];
let label_face = load_font(PathBuf::from("/pkg/data/fonts/Roboto-Regular.ttf"))?;
let (title, para) = Self::new_sample_pair();
Ok(Box::new(Self {
app_context: app_context.clone(),
view_key,
sample_title: title,
sample_paragraph: para,
sample_faces,
sample_index: 0,
sample_size_divisor: 6.0,
label_face,
scene_details: None,
line_color: Color::new(),
round_scene_corners: true,
}))
}
fn new_sample_pair() -> (String, String) {
(lipsum_words(2), lipsum_words(25))
}
fn update(&mut self) {
self.scene_details = None;
self.app_context.request_render(self.view_key);
}
fn new_sample_text(&mut self) {
let (title, para) = Self::new_sample_pair();
self.sample_title = title;
self.sample_paragraph = para;
if let Some(scene_details) = self.scene_details.as_mut() {
scene_details.scene.send_message(
&scene_details.sample_title.clone(),
Box::new(SetTextMessage { text: self.sample_title.clone() }),
);
scene_details.scene.send_message(
&scene_details.sample_paragraph.clone(),
Box::new(SetTextMessage { text: self.sample_paragraph.clone() }),
);
}
self.app_context.request_render(self.view_key);
}
fn show_next_face(&mut self) {
self.sample_index = (self.sample_index + 1) % self.sample_faces.len();
self.update();
}
fn increase_sample_size(&mut self) {
self.sample_size_divisor = (self.sample_size_divisor - 0.5).max(1.0);
self.update();
}
fn decrease_sample_size(&mut self) {
self.sample_size_divisor = (self.sample_size_divisor + 0.5).min(10.0);
self.update();
}
fn toggle_line_color(&mut self) {
let black = Color::new();
if self.line_color == black {
self.line_color = Color::white();
} else {
self.line_color = black;
}
if let Some(scene_details) = self.scene_details.as_mut() {
let targets: Vec<FacetId> = scene_details.lines.iter().cloned().collect();
for target in targets {
scene_details
.scene
.send_message(&target, Box::new(SetColorMessage { color: self.line_color }));
}
}
self.app_context.request_render(self.view_key);
}
fn toggle_round_scene_corners(&mut self) {
self.round_scene_corners = !self.round_scene_corners;
if let Some(scene_details) = self.scene_details.as_mut() {
scene_details.scene.round_scene_corners(self.round_scene_corners);
}
self.app_context.request_render(self.view_key);
}
}
impl ViewAssistant for FontMetricsViewAssistant {
fn resize(&mut self, _new_size: &Size) -> Result<(), Error> {
self.scene_details = None;
Ok(())
}
fn render(
&mut self,
render_context: &mut RenderContext,
ready_event: Event,
context: &ViewAssistantContext,
) -> Result<(), Error> {
let mut scene_details = self.scene_details.take().unwrap_or_else(|| {
let mut builder = SceneBuilder::new(Color::white());
builder.round_scene_corners(self.round_scene_corners);
let size = context.size;
let text_size = size.height.min(size.width) / self.sample_size_divisor;
let ascent = self.sample_faces[self.sample_index].ascent(text_size);
let descent = self.sample_faces[self.sample_index].descent(text_size);
let sample_text_size = text_size / 3.0;
let baseline_location = size.height / 3.0;
let baseline_left = size.width * BASELINE_INDENT;
let baseline_width = size.width * (1.0 - BASELINE_INDENT * 2.0);
let baseline_right = baseline_left + baseline_width;
let sample_title = builder.text(
self.sample_faces[self.sample_index].clone(),
&self.sample_title,
text_size,
point2(size.width / 2.0, baseline_location),
TextFacetOptions {
horizontal_alignment: TextHorizontalAlignment::Center,
..TextFacetOptions::default()
},
);
let paragraph_baseline_location = size.height / 2.0;
let sample_paragraph = builder.text(
self.sample_faces[self.sample_index].clone(),
&self.sample_paragraph,
sample_text_size,
point2(baseline_left, paragraph_baseline_location),
TextFacetOptions {
horizontal_alignment: TextHorizontalAlignment::Left,
max_width: Some(baseline_width),
..TextFacetOptions::default()
},
);
let mut lines = Vec::new();
lines.push(builder.h_line(
baseline_location,
baseline_left,
baseline_right,
LINE_THICKNESS,
self.line_color,
));
let ascent_x = baseline_left + size.width * BASELINE_INDENT / 5.0;
lines.push(builder.v_line(
ascent_x,
baseline_location - ascent,
baseline_location,
LINE_THICKNESS,
self.line_color,
));
lines.push(builder.v_line(
ascent_x,
baseline_location,
baseline_location - descent,
LINE_THICKNESS,
self.line_color,
));
let label_size = size.height.min(size.width) / 30.0;
builder.text(
self.label_face.clone(),
"baseline",
label_size,
point2(baseline_right + PADDING * size.width, baseline_location),
TextFacetOptions {
vertical_alignment: TextVerticalAlignment::Center,
..TextFacetOptions::default()
},
);
builder.text(
self.label_face.clone(),
"ascent",
label_size,
point2(ascent_x - PADDING * size.width, baseline_location - ascent / 2.0),
TextFacetOptions {
vertical_alignment: TextVerticalAlignment::Center,
horizontal_alignment: TextHorizontalAlignment::Right,
..TextFacetOptions::default()
},
);
builder.text(
self.label_face.clone(),
"descent",
label_size,
point2(ascent_x - PADDING * size.width, baseline_location - descent / 2.0),
TextFacetOptions {
vertical_alignment: TextVerticalAlignment::Center,
horizontal_alignment: TextHorizontalAlignment::Right,
..TextFacetOptions::default()
},
);
let scene = builder.build();
SceneDetails { scene, sample_paragraph, sample_title, lines }
});
scene_details.scene.render(render_context, ready_event, context)?;
self.scene_details = Some(scene_details);
Ok(())
}
fn handle_keyboard_event(
&mut self,
_context: &mut ViewAssistantContext,
_event: &input::Event,
keyboard_event: &input::keyboard::Event,
) -> Result<(), Error> {
const C: u32 = 'c' as u32;
const F: u32 = 'f' as u32;
const K: u32 = 'k' as u32;
const T: u32 = 't' as u32;
const PLUS: u32 = '+' as u32;
const EQUALS: u32 = '=' as u32;
const MINUS: u32 = '-' as u32;
if let Some(code_point) = keyboard_event.code_point {
if keyboard_event.phase == input::keyboard::Phase::Pressed {
match code_point {
T => self.new_sample_text(),
F => self.show_next_face(),
PLUS | EQUALS => self.increase_sample_size(),
MINUS => self.decrease_sample_size(),
C => self.toggle_line_color(),
K => self.toggle_round_scene_corners(),
_ => println!("code_point = {}", code_point),
}
}
}
Ok(())
}
}
fn make_app_assistant_fut(
app_context: &AppContext,
) -> LocalBoxFuture<'_, Result<AppAssistantPtr, Error>> {
let f = async move {
let assistant = Box::new(FontAppAssistant { app_context: app_context.clone() });
Ok::<AppAssistantPtr, Error>(assistant)
};
Box::pin(f)
}
pub fn make_app_assistant() -> AssistantCreatorFunc {
Box::new(make_app_assistant_fut)
}
fn main() -> Result<(), Error> {
App::run(make_app_assistant())
}