2020-04-19 09:13:24 +00:00
|
|
|
use std::{collections::HashMap, sync::Arc};
|
2020-04-17 21:30:01 +00:00
|
|
|
|
|
|
|
use parking_lot::Mutex;
|
2020-04-17 13:33:52 +00:00
|
|
|
|
2020-04-19 09:13:24 +00:00
|
|
|
use crate::{layout::align_rect, *};
|
2020-04-17 13:33:52 +00:00
|
|
|
|
2020-05-08 20:25:28 +00:00
|
|
|
#[derive(Clone, Copy, Default)]
|
|
|
|
struct PaintStats {
|
|
|
|
num_batches: usize,
|
|
|
|
num_primitives: usize,
|
|
|
|
num_vertices: usize,
|
|
|
|
num_triangles: usize,
|
|
|
|
}
|
|
|
|
|
2020-04-17 13:33:52 +00:00
|
|
|
/// Contains the input, style and output of all GUI commands.
|
2020-05-08 20:42:31 +00:00
|
|
|
/// `Ui`:s keep an Arc pointer to this.
|
|
|
|
/// This allows us to create several child `Ui`:s at once,
|
2020-05-04 19:35:16 +00:00
|
|
|
/// all working against the same shared Context.
|
2020-04-17 13:33:52 +00:00
|
|
|
pub struct Context {
|
2020-05-08 20:42:31 +00:00
|
|
|
/// The default style for new `Ui`:s
|
2020-05-04 19:35:16 +00:00
|
|
|
style: Mutex<Style>,
|
2020-05-08 20:25:28 +00:00
|
|
|
mesher_options: Mutex<mesher::MesherOptions>,
|
2020-05-04 19:35:16 +00:00
|
|
|
fonts: Arc<Fonts>,
|
2020-05-08 20:25:28 +00:00
|
|
|
/// HACK: set a new font next frame
|
|
|
|
new_fonts: Mutex<Option<Arc<Fonts>>>,
|
|
|
|
memory: Arc<Mutex<Memory>>,
|
2020-05-04 19:35:16 +00:00
|
|
|
|
|
|
|
// Input releated stuff:
|
2020-04-28 21:05:22 +00:00
|
|
|
/// Raw input from last frame. Use `input()` instead.
|
2020-05-03 11:28:47 +00:00
|
|
|
last_raw_input: RawInput,
|
2020-05-04 19:35:16 +00:00
|
|
|
input: GuiInput,
|
2020-05-03 11:28:47 +00:00
|
|
|
mouse_tracker: MovementTracker<Pos2>,
|
2020-04-19 09:13:24 +00:00
|
|
|
|
2020-05-04 19:35:16 +00:00
|
|
|
// The output of a frame:
|
|
|
|
graphics: Mutex<GraphicLayers>,
|
2020-05-02 09:37:12 +00:00
|
|
|
output: Mutex<Output>,
|
2020-05-08 20:25:28 +00:00
|
|
|
/// Used to debug name clashes of e.g. windows
|
|
|
|
used_ids: Mutex<HashMap<Id, Pos2>>,
|
|
|
|
|
|
|
|
paint_stats: Mutex<PaintStats>,
|
2020-04-17 13:33:52 +00:00
|
|
|
}
|
|
|
|
|
2020-04-19 09:13:24 +00:00
|
|
|
// TODO: remove this impl.
|
2020-04-17 13:33:52 +00:00
|
|
|
impl Clone for Context {
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
Context {
|
|
|
|
style: Mutex::new(self.style()),
|
2020-05-08 20:25:28 +00:00
|
|
|
mesher_options: Mutex::new(*self.mesher_options.lock()),
|
2020-04-17 13:33:52 +00:00
|
|
|
fonts: self.fonts.clone(),
|
2020-05-08 20:25:28 +00:00
|
|
|
new_fonts: Mutex::new(self.new_fonts.lock().clone()),
|
|
|
|
memory: self.memory.clone(),
|
2020-04-28 21:05:22 +00:00
|
|
|
last_raw_input: self.last_raw_input.clone(),
|
|
|
|
input: self.input.clone(),
|
2020-05-03 11:28:47 +00:00
|
|
|
mouse_tracker: self.mouse_tracker.clone(),
|
2020-04-17 21:30:01 +00:00
|
|
|
graphics: Mutex::new(self.graphics.lock().clone()),
|
2020-04-23 17:15:17 +00:00
|
|
|
output: Mutex::new(self.output.lock().clone()),
|
2020-04-19 09:13:24 +00:00
|
|
|
used_ids: Mutex::new(self.used_ids.lock().clone()),
|
2020-05-08 20:25:28 +00:00
|
|
|
paint_stats: Mutex::new(*self.paint_stats.lock()),
|
2020-04-17 13:33:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Context {
|
2020-05-08 20:25:28 +00:00
|
|
|
pub fn new(pixels_per_point: f32) -> Arc<Context> {
|
|
|
|
Arc::new(Context {
|
2020-04-17 13:33:52 +00:00
|
|
|
style: Default::default(),
|
2020-05-08 20:25:28 +00:00
|
|
|
mesher_options: Default::default(),
|
2020-04-17 13:33:52 +00:00
|
|
|
fonts: Arc::new(Fonts::new(pixels_per_point)),
|
2020-05-08 20:25:28 +00:00
|
|
|
new_fonts: Default::default(),
|
|
|
|
memory: Default::default(),
|
|
|
|
|
2020-04-28 21:05:22 +00:00
|
|
|
last_raw_input: Default::default(),
|
2020-04-17 13:33:52 +00:00
|
|
|
input: Default::default(),
|
2020-05-03 11:28:47 +00:00
|
|
|
mouse_tracker: MovementTracker::new(1000, 0.1),
|
2020-05-08 20:25:28 +00:00
|
|
|
|
2020-04-17 13:33:52 +00:00
|
|
|
graphics: Default::default(),
|
2020-04-23 17:15:17 +00:00
|
|
|
output: Default::default(),
|
2020-04-19 09:13:24 +00:00
|
|
|
used_ids: Default::default(),
|
2020-05-08 20:25:28 +00:00
|
|
|
paint_stats: Default::default(),
|
|
|
|
})
|
2020-04-17 13:33:52 +00:00
|
|
|
}
|
|
|
|
|
2020-05-07 08:47:03 +00:00
|
|
|
pub fn memory(&self) -> parking_lot::MutexGuard<'_, Memory> {
|
2020-05-02 09:37:12 +00:00
|
|
|
self.memory.lock()
|
2020-04-23 17:15:17 +00:00
|
|
|
}
|
|
|
|
|
2020-05-07 08:47:03 +00:00
|
|
|
pub fn graphics(&self) -> parking_lot::MutexGuard<'_, GraphicLayers> {
|
2020-05-04 19:35:16 +00:00
|
|
|
self.graphics.lock()
|
|
|
|
}
|
|
|
|
|
2020-05-07 08:47:03 +00:00
|
|
|
pub fn output(&self) -> parking_lot::MutexGuard<'_, Output> {
|
2020-05-02 09:37:12 +00:00
|
|
|
self.output.lock()
|
2020-05-01 00:08:01 +00:00
|
|
|
}
|
|
|
|
|
2020-05-02 09:37:12 +00:00
|
|
|
pub fn input(&self) -> &GuiInput {
|
|
|
|
&self.input
|
2020-05-01 00:08:01 +00:00
|
|
|
}
|
|
|
|
|
2020-05-03 11:28:47 +00:00
|
|
|
/// Smoothed mouse velocity, in points per second
|
|
|
|
pub fn mouse_vel(&self) -> Vec2 {
|
|
|
|
self.mouse_tracker.velocity().unwrap_or_default()
|
|
|
|
}
|
|
|
|
|
2020-04-28 21:05:22 +00:00
|
|
|
/// Raw input from last frame. Use `input()` instead.
|
|
|
|
pub fn last_raw_input(&self) -> &RawInput {
|
|
|
|
&self.last_raw_input
|
|
|
|
}
|
|
|
|
|
2020-05-04 19:35:16 +00:00
|
|
|
pub fn fonts(&self) -> &Fonts {
|
|
|
|
&*self.fonts
|
|
|
|
}
|
|
|
|
|
2020-05-08 20:25:28 +00:00
|
|
|
pub fn texture(&self) -> &Texture {
|
|
|
|
self.fonts().texture()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Will become active next frame
|
|
|
|
pub fn set_fonts(&self, fonts: Fonts) {
|
|
|
|
*self.new_fonts.lock() = Some(Arc::new(fonts));
|
2020-05-04 19:35:16 +00:00
|
|
|
}
|
|
|
|
|
2020-04-17 13:33:52 +00:00
|
|
|
pub fn style(&self) -> Style {
|
2020-04-17 21:30:01 +00:00
|
|
|
*self.style.lock()
|
2020-04-17 13:33:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_style(&self, style: Style) {
|
2020-04-17 21:30:01 +00:00
|
|
|
*self.style.lock() = style;
|
2020-04-17 13:33:52 +00:00
|
|
|
}
|
|
|
|
|
2020-05-03 11:28:47 +00:00
|
|
|
pub fn pixels_per_point(&self) -> f32 {
|
|
|
|
self.input.pixels_per_point
|
|
|
|
}
|
|
|
|
|
2020-05-02 09:37:12 +00:00
|
|
|
/// Useful for pixel-perfect rendering
|
|
|
|
pub fn round_to_pixel(&self, point: f32) -> f32 {
|
|
|
|
(point * self.input.pixels_per_point).round() / self.input.pixels_per_point
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn round_pos_to_pixels(&self, pos: Pos2) -> Pos2 {
|
|
|
|
pos2(self.round_to_pixel(pos.x), self.round_to_pixel(pos.y))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn round_vec_to_pixels(&self, vec: Vec2) -> Vec2 {
|
|
|
|
vec2(self.round_to_pixel(vec.x), self.round_to_pixel(vec.y))
|
|
|
|
}
|
|
|
|
|
2020-05-08 20:25:28 +00:00
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
|
|
|
pub fn begin_frame(self: &mut Arc<Self>, new_input: RawInput) {
|
|
|
|
let mut self_: Self = (**self).clone();
|
|
|
|
self_.begin_frame_mut(new_input);
|
|
|
|
*self = Arc::new(self_);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn begin_frame_mut(&mut self, new_input: RawInput) {
|
2020-05-03 11:28:47 +00:00
|
|
|
if !self.last_raw_input.mouse_down || self.last_raw_input.mouse_pos.is_none() {
|
|
|
|
self.memory().active_id = None;
|
|
|
|
}
|
2020-05-10 08:32:28 +00:00
|
|
|
self.memory().begin_frame();
|
2020-05-03 11:28:47 +00:00
|
|
|
|
2020-04-19 09:13:24 +00:00
|
|
|
self.used_ids.lock().clear();
|
2020-05-03 11:28:47 +00:00
|
|
|
|
2020-05-08 20:25:28 +00:00
|
|
|
if let Some(new_fonts) = self.new_fonts.lock().take() {
|
|
|
|
self.fonts = new_fonts;
|
|
|
|
}
|
|
|
|
|
2020-05-03 11:28:47 +00:00
|
|
|
if let Some(mouse_pos) = new_input.mouse_pos {
|
|
|
|
self.mouse_tracker.add(new_input.time, mouse_pos);
|
|
|
|
} else {
|
|
|
|
self.mouse_tracker.clear();
|
|
|
|
}
|
|
|
|
self.input = GuiInput::from_last_and_new(&self.last_raw_input, &new_input);
|
|
|
|
self.input.mouse_velocity = self.mouse_vel();
|
|
|
|
self.last_raw_input = new_input;
|
2020-04-23 17:15:17 +00:00
|
|
|
}
|
|
|
|
|
2020-05-08 20:25:28 +00:00
|
|
|
pub fn end_frame(&self) -> (Output, PaintBatches) {
|
|
|
|
let output: Output = std::mem::take(&mut self.output());
|
|
|
|
let paint_batches = self.paint();
|
|
|
|
(output, paint_batches)
|
2020-04-17 13:33:52 +00:00
|
|
|
}
|
|
|
|
|
2020-05-08 20:25:28 +00:00
|
|
|
fn drain_paint_lists(&self) -> Vec<(Rect, PaintCmd)> {
|
2020-05-04 19:35:16 +00:00
|
|
|
let memory = self.memory();
|
2020-05-10 11:14:52 +00:00
|
|
|
self.graphics().drain(memory.area_order()).collect()
|
2020-04-17 21:22:28 +00:00
|
|
|
}
|
|
|
|
|
2020-05-08 20:25:28 +00:00
|
|
|
fn paint(&self) -> PaintBatches {
|
|
|
|
let mut mesher_options = *self.mesher_options.lock();
|
|
|
|
mesher_options.aa_size = 1.0 / self.pixels_per_point();
|
|
|
|
let paint_commands = self.drain_paint_lists();
|
|
|
|
let num_primitives = paint_commands.len();
|
|
|
|
let batches = mesher::mesh_paint_commands(mesher_options, self.fonts(), paint_commands);
|
|
|
|
|
|
|
|
{
|
|
|
|
let mut stats = PaintStats::default();
|
|
|
|
stats.num_batches = batches.len();
|
|
|
|
stats.num_primitives = num_primitives;
|
|
|
|
for (_, mesh) in &batches {
|
|
|
|
stats.num_vertices += mesh.vertices.len();
|
|
|
|
stats.num_triangles += mesh.indices.len() / 3;
|
|
|
|
}
|
|
|
|
*self.paint_stats.lock() = stats;
|
|
|
|
}
|
|
|
|
|
|
|
|
batches
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
2020-05-10 11:14:52 +00:00
|
|
|
/// A `Ui` for the entire screen, behind any windows.
|
2020-05-08 20:42:31 +00:00
|
|
|
pub fn fullscreen_ui(self: &Arc<Self>) -> Ui {
|
2020-05-08 20:25:28 +00:00
|
|
|
let rect = Rect::from_min_size(Default::default(), self.input().screen_size);
|
2020-05-08 20:42:31 +00:00
|
|
|
Ui::new(self.clone(), Layer::Background, Id::background(), rect)
|
2020-05-08 20:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
2020-04-17 13:33:52 +00:00
|
|
|
/// Is the user interacting with anything?
|
|
|
|
pub fn any_active(&self) -> bool {
|
2020-05-04 19:35:16 +00:00
|
|
|
self.memory().active_id.is_some()
|
2020-04-17 13:33:52 +00:00
|
|
|
}
|
|
|
|
|
2020-04-19 09:13:24 +00:00
|
|
|
/// Generate a id from the given source.
|
|
|
|
/// If it is not unique, an error will be printed at the given position.
|
2020-04-25 13:26:24 +00:00
|
|
|
pub fn make_unique_id<IdSource>(&self, source: IdSource, pos: Pos2) -> Id
|
2020-04-19 09:13:24 +00:00
|
|
|
where
|
2020-04-25 13:26:24 +00:00
|
|
|
IdSource: std::hash::Hash + std::fmt::Debug + Copy,
|
2020-04-19 09:13:24 +00:00
|
|
|
{
|
|
|
|
self.register_unique_id(Id::new(source), source, pos)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// If the given Id is not unique, an error will be printed at the given position.
|
2020-04-25 13:26:24 +00:00
|
|
|
pub fn register_unique_id(&self, id: Id, source_name: impl std::fmt::Debug, pos: Pos2) -> Id {
|
2020-04-19 09:13:24 +00:00
|
|
|
if let Some(clash_pos) = self.used_ids.lock().insert(id, pos) {
|
|
|
|
if clash_pos.dist(pos) < 4.0 {
|
|
|
|
self.show_error(
|
|
|
|
pos,
|
|
|
|
&format!("use of non-unique ID {:?} (name clash?)", source_name),
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
self.show_error(
|
|
|
|
clash_pos,
|
|
|
|
&format!("first use of non-unique ID {:?} (name clash?)", source_name),
|
|
|
|
);
|
|
|
|
self.show_error(
|
|
|
|
pos,
|
|
|
|
&format!(
|
|
|
|
"second use of non-unique ID {:?} (name clash?)",
|
|
|
|
source_name
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
id
|
|
|
|
} else {
|
|
|
|
id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 17:12:00 +00:00
|
|
|
pub fn contains_mouse(&self, layer: Layer, clip_rect: Rect, rect: Rect) -> bool {
|
2020-04-26 20:30:24 +00:00
|
|
|
let rect = rect.intersect(clip_rect);
|
2020-04-22 18:01:49 +00:00
|
|
|
if let Some(mouse_pos) = self.input.mouse_pos {
|
2020-05-04 19:35:16 +00:00
|
|
|
rect.contains(mouse_pos) && layer == self.memory().layer_at(mouse_pos)
|
2020-04-17 13:33:52 +00:00
|
|
|
} else {
|
|
|
|
false
|
2020-04-22 18:01:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-26 20:30:24 +00:00
|
|
|
pub fn interact(
|
|
|
|
&self,
|
|
|
|
layer: Layer,
|
2020-05-05 17:12:00 +00:00
|
|
|
clip_rect: Rect,
|
|
|
|
rect: Rect,
|
2020-04-26 20:30:24 +00:00
|
|
|
interaction_id: Option<Id>,
|
|
|
|
) -> InteractInfo {
|
2020-05-05 17:12:00 +00:00
|
|
|
let hovered = self.contains_mouse(layer, clip_rect, rect);
|
2020-04-21 18:43:47 +00:00
|
|
|
|
2020-05-04 19:35:16 +00:00
|
|
|
let mut memory = self.memory();
|
2020-04-21 18:43:47 +00:00
|
|
|
let active = interaction_id.is_some() && memory.active_id == interaction_id;
|
|
|
|
|
2020-04-22 18:01:49 +00:00
|
|
|
if self.input.mouse_pressed {
|
2020-04-21 18:43:47 +00:00
|
|
|
if hovered && interaction_id.is_some() {
|
|
|
|
if memory.active_id.is_some() {
|
|
|
|
// Already clicked something else this frame
|
|
|
|
InteractInfo {
|
2020-05-05 17:12:00 +00:00
|
|
|
rect,
|
2020-04-21 18:43:47 +00:00
|
|
|
hovered,
|
|
|
|
clicked: false,
|
|
|
|
active: false,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
memory.active_id = interaction_id;
|
|
|
|
InteractInfo {
|
2020-05-05 17:12:00 +00:00
|
|
|
rect,
|
2020-04-21 18:43:47 +00:00
|
|
|
hovered,
|
|
|
|
clicked: false,
|
|
|
|
active: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
InteractInfo {
|
2020-05-05 17:12:00 +00:00
|
|
|
rect,
|
2020-04-21 18:43:47 +00:00
|
|
|
hovered,
|
|
|
|
clicked: false,
|
|
|
|
active: false,
|
|
|
|
}
|
|
|
|
}
|
2020-04-22 18:01:49 +00:00
|
|
|
} else if self.input.mouse_released {
|
2020-04-21 18:43:47 +00:00
|
|
|
InteractInfo {
|
2020-05-05 17:12:00 +00:00
|
|
|
rect,
|
2020-04-21 18:43:47 +00:00
|
|
|
hovered,
|
|
|
|
clicked: hovered && active,
|
|
|
|
active,
|
|
|
|
}
|
2020-04-22 18:01:49 +00:00
|
|
|
} else if self.input.mouse_down {
|
2020-04-21 18:43:47 +00:00
|
|
|
InteractInfo {
|
2020-05-05 17:12:00 +00:00
|
|
|
rect,
|
2020-04-21 18:43:47 +00:00
|
|
|
hovered: hovered && active,
|
|
|
|
clicked: false,
|
|
|
|
active,
|
2020-04-17 13:33:52 +00:00
|
|
|
}
|
|
|
|
} else {
|
2020-04-21 18:43:47 +00:00
|
|
|
InteractInfo {
|
2020-05-05 17:12:00 +00:00
|
|
|
rect,
|
2020-04-21 18:43:47 +00:00
|
|
|
hovered,
|
|
|
|
clicked: false,
|
|
|
|
active,
|
|
|
|
}
|
2020-04-17 13:33:52 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-19 09:13:24 +00:00
|
|
|
|
2020-05-08 20:25:28 +00:00
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
2020-04-19 09:13:24 +00:00
|
|
|
pub fn show_error(&self, pos: Pos2, text: &str) {
|
|
|
|
let align = (Align::Min, Align::Min);
|
2020-04-27 14:53:14 +00:00
|
|
|
let layer = Layer::Debug;
|
2020-04-19 09:13:24 +00:00
|
|
|
let text_style = TextStyle::Monospace;
|
|
|
|
let font = &self.fonts[text_style];
|
2020-04-25 08:52:20 +00:00
|
|
|
let (text, size) = font.layout_multiline(text, f32::INFINITY);
|
2020-05-05 17:12:00 +00:00
|
|
|
let rect = align_rect(Rect::from_min_size(pos, size), align);
|
2020-04-19 09:13:24 +00:00
|
|
|
self.add_paint_cmd(
|
|
|
|
layer,
|
|
|
|
PaintCmd::Rect {
|
|
|
|
corner_radius: 0.0,
|
|
|
|
fill_color: Some(color::gray(0, 240)),
|
|
|
|
outline: Some(Outline::new(1.0, color::RED)),
|
|
|
|
rect: rect.expand(2.0),
|
|
|
|
},
|
|
|
|
);
|
2020-04-25 13:45:38 +00:00
|
|
|
self.add_text(layer, rect.min, text_style, text, Some(color::RED));
|
2020-04-19 09:13:24 +00:00
|
|
|
}
|
|
|
|
|
2020-04-25 12:37:39 +00:00
|
|
|
pub fn debug_text(&self, pos: Pos2, text: &str) {
|
2020-04-27 14:53:14 +00:00
|
|
|
let layer = Layer::Debug;
|
2020-04-25 12:37:39 +00:00
|
|
|
let align = (Align::Min, Align::Min);
|
|
|
|
self.floating_text(
|
|
|
|
layer,
|
|
|
|
pos,
|
|
|
|
text,
|
|
|
|
TextStyle::Monospace,
|
|
|
|
align,
|
|
|
|
Some(color::YELLOW),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-05-05 17:33:02 +00:00
|
|
|
pub fn debug_rect(&self, rect: Rect, text: &str) {
|
|
|
|
let layer = Layer::Debug;
|
|
|
|
self.add_paint_cmd(
|
|
|
|
layer,
|
|
|
|
PaintCmd::Rect {
|
|
|
|
corner_radius: 0.0,
|
|
|
|
fill_color: None,
|
|
|
|
outline: Some(Outline::new(1.0, color::RED)),
|
|
|
|
rect,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
let align = (Align::Min, Align::Min);
|
|
|
|
let text_style = TextStyle::Monospace;
|
|
|
|
self.floating_text(layer, rect.min, text, text_style, align, Some(color::RED));
|
|
|
|
}
|
|
|
|
|
2020-04-19 09:13:24 +00:00
|
|
|
/// Show some text anywhere on screen.
|
|
|
|
/// To center the text at the given position, use `align: (Center, Center)`.
|
|
|
|
pub fn floating_text(
|
|
|
|
&self,
|
|
|
|
layer: Layer,
|
|
|
|
pos: Pos2,
|
|
|
|
text: &str,
|
|
|
|
text_style: TextStyle,
|
|
|
|
align: (Align, Align),
|
|
|
|
text_color: Option<Color>,
|
|
|
|
) -> Vec2 {
|
|
|
|
let font = &self.fonts[text_style];
|
2020-04-25 08:52:20 +00:00
|
|
|
let (text, size) = font.layout_multiline(text, f32::INFINITY);
|
2020-05-05 17:12:00 +00:00
|
|
|
let rect = align_rect(Rect::from_min_size(pos, size), align);
|
2020-04-25 13:45:38 +00:00
|
|
|
self.add_text(layer, rect.min, text_style, text, text_color);
|
2020-04-19 09:13:24 +00:00
|
|
|
size
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Already layed out text.
|
|
|
|
pub fn add_text(
|
|
|
|
&self,
|
|
|
|
layer: Layer,
|
|
|
|
pos: Pos2,
|
|
|
|
text_style: TextStyle,
|
|
|
|
text: Vec<font::TextFragment>,
|
|
|
|
color: Option<Color>,
|
|
|
|
) {
|
|
|
|
let color = color.unwrap_or_else(|| self.style().text_color());
|
|
|
|
for fragment in text {
|
|
|
|
self.add_paint_cmd(
|
|
|
|
layer,
|
|
|
|
PaintCmd::Text {
|
|
|
|
color,
|
|
|
|
pos: pos + vec2(0.0, fragment.y_offset),
|
|
|
|
text: fragment.text,
|
|
|
|
text_style,
|
|
|
|
x_offsets: fragment.x_offsets,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add_paint_cmd(&self, layer: Layer, paint_cmd: PaintCmd) {
|
2020-05-04 19:35:16 +00:00
|
|
|
self.graphics()
|
2020-04-20 21:33:16 +00:00
|
|
|
.layer(layer)
|
|
|
|
.push((Rect::everything(), paint_cmd))
|
2020-04-19 09:13:24 +00:00
|
|
|
}
|
2020-04-17 13:33:52 +00:00
|
|
|
}
|
|
|
|
|
2020-05-08 20:25:28 +00:00
|
|
|
impl Context {
|
2020-05-08 20:42:31 +00:00
|
|
|
pub fn ui(&self, ui: &mut Ui) {
|
2020-05-08 20:25:28 +00:00
|
|
|
use crate::containers::*;
|
|
|
|
|
2020-05-08 20:42:31 +00:00
|
|
|
ui.collapsing("Style", |ui| {
|
|
|
|
self.mesher_options.lock().ui(ui);
|
|
|
|
self.style_ui(ui);
|
2020-05-08 20:25:28 +00:00
|
|
|
});
|
|
|
|
|
2020-05-08 20:42:31 +00:00
|
|
|
ui.collapsing("Fonts", |ui| {
|
2020-05-08 20:25:28 +00:00
|
|
|
let old_font_definitions = self.fonts().definitions();
|
|
|
|
let mut new_font_definitions = old_font_definitions.clone();
|
2020-05-08 20:42:31 +00:00
|
|
|
font_definitions_ui(&mut new_font_definitions, ui);
|
|
|
|
self.fonts().texture().ui(ui);
|
2020-05-08 20:25:28 +00:00
|
|
|
if *old_font_definitions != new_font_definitions {
|
|
|
|
let fonts =
|
|
|
|
Fonts::from_definitions(new_font_definitions, self.input().pixels_per_point);
|
|
|
|
self.set_fonts(fonts);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-05-08 20:42:31 +00:00
|
|
|
ui.collapsing("Input", |ui| {
|
2020-05-08 20:25:28 +00:00
|
|
|
CollapsingHeader::new("Raw Input")
|
|
|
|
.default_open()
|
2020-05-08 20:42:31 +00:00
|
|
|
.show(ui, |ui| ui.ctx().last_raw_input().clone().ui(ui));
|
2020-05-08 20:25:28 +00:00
|
|
|
CollapsingHeader::new("Input")
|
|
|
|
.default_open()
|
2020-05-08 20:42:31 +00:00
|
|
|
.show(ui, |ui| ui.input().clone().ui(ui));
|
2020-05-08 20:25:28 +00:00
|
|
|
});
|
|
|
|
|
2020-05-08 20:42:31 +00:00
|
|
|
ui.collapsing("Stats", |ui| {
|
|
|
|
ui.add(label!(
|
2020-05-08 20:25:28 +00:00
|
|
|
"Screen size: {} x {} points, pixels_per_point: {}",
|
2020-05-08 20:42:31 +00:00
|
|
|
ui.input().screen_size.x,
|
|
|
|
ui.input().screen_size.y,
|
|
|
|
ui.input().pixels_per_point,
|
2020-05-08 20:25:28 +00:00
|
|
|
));
|
2020-05-08 20:42:31 +00:00
|
|
|
if let Some(mouse_pos) = ui.input().mouse_pos {
|
|
|
|
ui.add(label!("mouse_pos: {:.2} x {:.2}", mouse_pos.x, mouse_pos.y,));
|
2020-05-08 20:25:28 +00:00
|
|
|
} else {
|
2020-05-08 20:42:31 +00:00
|
|
|
ui.add_label("mouse_pos: None");
|
2020-05-08 20:25:28 +00:00
|
|
|
}
|
|
|
|
|
2020-05-08 20:42:31 +00:00
|
|
|
ui.add(label!("Painting:").text_style(TextStyle::Heading));
|
|
|
|
self.paint_stats.lock().ui(ui);
|
2020-05-08 20:25:28 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-08 20:42:31 +00:00
|
|
|
fn font_definitions_ui(font_definitions: &mut FontDefinitions, ui: &mut Ui) {
|
2020-05-08 20:25:28 +00:00
|
|
|
use crate::widgets::*;
|
|
|
|
for (text_style, (_family, size)) in font_definitions.iter_mut() {
|
|
|
|
// TODO: radiobutton for family
|
2020-05-08 20:42:31 +00:00
|
|
|
ui.add(
|
2020-05-08 20:25:28 +00:00
|
|
|
Slider::f32(size, 4.0..=40.0)
|
|
|
|
.precision(0)
|
|
|
|
.text(format!("{:?}", text_style)),
|
|
|
|
);
|
|
|
|
}
|
2020-05-08 20:42:31 +00:00
|
|
|
if ui.add(Button::new("Reset fonts")).clicked {
|
2020-05-08 20:25:28 +00:00
|
|
|
*font_definitions = crate::fonts::default_font_definitions();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-17 13:33:52 +00:00
|
|
|
impl Context {
|
2020-05-08 20:42:31 +00:00
|
|
|
pub fn style_ui(&self, ui: &mut Ui) {
|
2020-04-17 13:33:52 +00:00
|
|
|
let mut style = self.style();
|
2020-05-08 20:42:31 +00:00
|
|
|
style.ui(ui);
|
2020-04-17 13:33:52 +00:00
|
|
|
self.set_style(style);
|
|
|
|
}
|
|
|
|
}
|
2020-05-08 20:25:28 +00:00
|
|
|
|
|
|
|
impl mesher::MesherOptions {
|
2020-05-08 20:42:31 +00:00
|
|
|
pub fn ui(&mut self, ui: &mut Ui) {
|
2020-05-08 20:25:28 +00:00
|
|
|
use crate::widgets::*;
|
2020-05-08 20:42:31 +00:00
|
|
|
ui.add(Checkbox::new(&mut self.anti_alias, "Antialias"));
|
|
|
|
ui.add(Checkbox::new(
|
2020-05-08 20:25:28 +00:00
|
|
|
&mut self.debug_paint_clip_rects,
|
|
|
|
"Paint Clip Rects (debug)",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PaintStats {
|
2020-05-08 20:42:31 +00:00
|
|
|
pub fn ui(&self, ui: &mut Ui) {
|
|
|
|
ui.add(label!("Batches: {}", self.num_batches))
|
2020-05-08 20:25:28 +00:00
|
|
|
.tooltip_text("Number of separate clip rectanlges");
|
2020-05-08 20:42:31 +00:00
|
|
|
ui.add(label!("Primitives: {}", self.num_primitives))
|
2020-05-08 20:25:28 +00:00
|
|
|
.tooltip_text("Boxes, circles, text areas etc");
|
2020-05-08 20:42:31 +00:00
|
|
|
ui.add(label!("Vertices: {}", self.num_vertices));
|
|
|
|
ui.add(label!("Triangles: {}", self.num_triangles));
|
2020-05-08 20:25:28 +00:00
|
|
|
}
|
|
|
|
}
|