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-19 18:54:02 +00:00
|
|
|
paint_options: Mutex<mesher::PaintOptions>,
|
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-05-12 15:09:54 +00:00
|
|
|
raw_input: RawInput,
|
|
|
|
previus_input: GuiInput,
|
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-19 18:54:02 +00:00
|
|
|
paint_options: Mutex::new(*self.paint_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-05-12 15:09:54 +00:00
|
|
|
raw_input: self.raw_input.clone(),
|
|
|
|
previus_input: self.previus_input.clone(),
|
2020-04-28 21:05:22 +00:00
|
|
|
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-19 18:54:02 +00:00
|
|
|
paint_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-05-12 15:09:54 +00:00
|
|
|
raw_input: Default::default(),
|
|
|
|
previus_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-11 18:21:24 +00:00
|
|
|
pub fn rect(&self) -> Rect {
|
|
|
|
Rect::from_min_size(pos2(0.0, 0.0), self.input.screen_size)
|
|
|
|
}
|
|
|
|
|
2020-05-07 08:47:03 +00:00
|
|
|
pub fn memory(&self) -> parking_lot::MutexGuard<'_, Memory> {
|
2020-05-17 14:42:46 +00:00
|
|
|
self.memory.try_lock().expect("memory already locked")
|
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-17 14:42:46 +00:00
|
|
|
self.graphics.try_lock().expect("graphics already locked")
|
2020-05-04 19:35:16 +00:00
|
|
|
}
|
|
|
|
|
2020-05-07 08:47:03 +00:00
|
|
|
pub fn output(&self) -> parking_lot::MutexGuard<'_, Output> {
|
2020-05-17 14:42:46 +00:00
|
|
|
self.output.try_lock().expect("output already locked")
|
2020-05-01 00:08:01 +00:00
|
|
|
}
|
|
|
|
|
2020-05-12 15:09:54 +00:00
|
|
|
/// Input previous frame. Compare to `input()` to check for changes.
|
|
|
|
pub fn previus_input(&self) -> &GuiInput {
|
|
|
|
&self.previus_input
|
|
|
|
}
|
|
|
|
|
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-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-05-10 12:27:02 +00:00
|
|
|
// TODO: return MutexGuard
|
2020-04-17 13:33:52 +00:00
|
|
|
pub fn style(&self) -> Style {
|
2020-05-17 14:42:46 +00:00
|
|
|
*self.style.try_lock().expect("style already locked")
|
2020-04-17 13:33:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_style(&self, style: Style) {
|
2020-05-17 14:42:46 +00:00
|
|
|
*self.style.try_lock().expect("style already locked") = 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-17 14:42:20 +00:00
|
|
|
pub fn round_rect_to_pixels(&self, rect: Rect) -> Rect {
|
|
|
|
Rect {
|
|
|
|
min: self.round_pos_to_pixels(rect.min),
|
|
|
|
max: self.round_pos_to_pixels(rect.max),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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_);
|
|
|
|
}
|
|
|
|
|
2020-05-12 15:09:54 +00:00
|
|
|
fn begin_frame_mut(&mut self, new_raw_input: RawInput) {
|
|
|
|
if !self.raw_input.mouse_down || self.raw_input.mouse_pos.is_none() {
|
2020-05-03 11:28:47 +00:00
|
|
|
self.memory().active_id = None;
|
2020-05-17 15:44:18 +00:00
|
|
|
|
|
|
|
let window_interaction = self.memory().window_interaction.take();
|
|
|
|
if let Some(window_interaction) = window_interaction {
|
|
|
|
let area_layer = window_interaction.area_layer;
|
|
|
|
let area_state = self.memory().areas.get(area_layer.id).clone();
|
|
|
|
if let Some(mut area_state) = area_state {
|
|
|
|
// Throw windows because it is fun:
|
|
|
|
area_state.vel = self.input().mouse_velocity;
|
|
|
|
self.memory().areas.set_state(area_layer, area_state);
|
|
|
|
}
|
|
|
|
}
|
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-12 15:09:54 +00:00
|
|
|
if let Some(mouse_pos) = new_raw_input.mouse_pos {
|
|
|
|
self.mouse_tracker.add(new_raw_input.time, mouse_pos);
|
2020-05-03 11:28:47 +00:00
|
|
|
} else {
|
2020-05-17 14:42:20 +00:00
|
|
|
// we do not clear the `mouse_tracker` here, because it is exactly when a finger has
|
|
|
|
// released from the touch screen that we may want to assign a velocity to whatever
|
|
|
|
// the user tried to throw
|
2020-05-03 11:28:47 +00:00
|
|
|
}
|
2020-05-12 15:09:54 +00:00
|
|
|
let new_input = GuiInput::from_last_and_new(&self.raw_input, &new_raw_input);
|
|
|
|
self.previus_input = std::mem::replace(&mut self.input, new_input);
|
2020-05-03 11:28:47 +00:00
|
|
|
self.input.mouse_velocity = self.mouse_vel();
|
2020-05-12 15:09:54 +00:00
|
|
|
self.raw_input = new_raw_input;
|
2020-04-23 17:15:17 +00:00
|
|
|
}
|
|
|
|
|
2020-05-08 20:25:28 +00:00
|
|
|
pub fn end_frame(&self) -> (Output, PaintBatches) {
|
2020-05-12 14:49:43 +00:00
|
|
|
self.memory().end_frame();
|
2020-05-08 20:25:28 +00:00
|
|
|
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 17:02:17 +00:00
|
|
|
self.graphics().drain(memory.areas.order()).collect()
|
2020-04-17 21:22:28 +00:00
|
|
|
}
|
|
|
|
|
2020-05-08 20:25:28 +00:00
|
|
|
fn paint(&self) -> PaintBatches {
|
2020-05-19 18:54:02 +00:00
|
|
|
let mut paint_options = *self.paint_options.lock();
|
|
|
|
paint_options.aa_size = 1.0 / self.pixels_per_point();
|
|
|
|
paint_options.aa_size *= 1.5; // Looks better, but TODO: should not be needed
|
2020-05-08 20:25:28 +00:00
|
|
|
let paint_commands = self.drain_paint_lists();
|
|
|
|
let num_primitives = paint_commands.len();
|
2020-05-19 18:54:02 +00:00
|
|
|
let batches =
|
|
|
|
mesher::paint_commands_into_triangles(paint_options, self.fonts(), paint_commands);
|
2020-05-08 20:25:28 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
let mut stats = PaintStats::default();
|
|
|
|
stats.num_batches = batches.len();
|
|
|
|
stats.num_primitives = num_primitives;
|
2020-05-19 18:54:02 +00:00
|
|
|
for (_, triangles) in &batches {
|
|
|
|
stats.num_vertices += triangles.vertices.len();
|
|
|
|
stats.num_triangles += triangles.indices.len() / 3;
|
2020-05-08 20:25:28 +00:00
|
|
|
}
|
|
|
|
*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-10 12:27:02 +00:00
|
|
|
let id = Id::background();
|
|
|
|
let layer = Layer {
|
|
|
|
order: Order::Background,
|
|
|
|
id,
|
|
|
|
};
|
|
|
|
// Ensure we register the background area so it is painted:
|
2020-05-10 17:02:17 +00:00
|
|
|
self.memory().areas.set_state(
|
2020-05-10 12:27:02 +00:00
|
|
|
layer,
|
|
|
|
containers::area::State {
|
|
|
|
pos: rect.min,
|
|
|
|
size: rect.size(),
|
|
|
|
interactable: true,
|
|
|
|
vel: Default::default(),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
Ui::new(self.clone(), layer, id, 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-10 12:27:02 +00:00
|
|
|
rect.contains(mouse_pos) && self.memory().layer_at(mouse_pos) == Some(layer)
|
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-17 10:26:17 +00:00
|
|
|
let interact_rect = rect.expand2(0.5 * self.style().item_spacing); // make it easier to click. TODO: nice way to do this
|
|
|
|
let hovered = self.contains_mouse(layer, clip_rect, interact_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-05-10 12:27:02 +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-05-16 09:27:02 +00:00
|
|
|
let galley = font.layout_multiline(text, f32::INFINITY);
|
|
|
|
let rect = align_rect(Rect::from_min_size(pos, galley.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-05-16 16:17:35 +00:00
|
|
|
self.add_galley(layer, rect.min, galley, text_style, 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-05-10 12:27:02 +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) {
|
2020-05-10 12:27:02 +00:00
|
|
|
let layer = Layer::debug();
|
2020-05-05 17:33:02 +00:00
|
|
|
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>,
|
2020-05-16 16:17:35 +00:00
|
|
|
) -> Rect {
|
2020-04-19 09:13:24 +00:00
|
|
|
let font = &self.fonts[text_style];
|
2020-05-16 09:27:02 +00:00
|
|
|
let galley = font.layout_multiline(text, f32::INFINITY);
|
|
|
|
let rect = align_rect(Rect::from_min_size(pos, galley.size), align);
|
2020-05-16 16:17:35 +00:00
|
|
|
self.add_galley(layer, rect.min, galley, text_style, text_color);
|
|
|
|
rect
|
2020-04-19 09:13:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Already layed out text.
|
2020-05-16 16:17:35 +00:00
|
|
|
pub fn add_galley(
|
2020-04-19 09:13:24 +00:00
|
|
|
&self,
|
|
|
|
layer: Layer,
|
|
|
|
pos: Pos2,
|
2020-05-16 16:17:35 +00:00
|
|
|
galley: font::Galley,
|
2020-04-19 09:13:24 +00:00
|
|
|
text_style: TextStyle,
|
|
|
|
color: Option<Color>,
|
|
|
|
) {
|
2020-05-17 07:44:09 +00:00
|
|
|
let color = color.unwrap_or_else(|| self.style().text_color);
|
2020-05-16 16:17:35 +00:00
|
|
|
self.add_paint_cmd(
|
|
|
|
layer,
|
|
|
|
PaintCmd::Text {
|
|
|
|
pos,
|
|
|
|
galley,
|
|
|
|
text_style,
|
|
|
|
color,
|
|
|
|
},
|
|
|
|
);
|
2020-04-19 09:13:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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-10 17:04:10 +00:00
|
|
|
pub fn settings_ui(&self, ui: &mut Ui) {
|
2020-05-08 20:25:28 +00:00
|
|
|
use crate::containers::*;
|
|
|
|
|
2020-05-10 17:04:10 +00:00
|
|
|
CollapsingHeader::new("Style")
|
|
|
|
// .default_open()
|
|
|
|
.show(ui, |ui| {
|
2020-05-19 18:54:02 +00:00
|
|
|
self.paint_options.lock().ui(ui);
|
2020-05-10 17:04:10 +00:00
|
|
|
self.style_ui(ui);
|
|
|
|
});
|
|
|
|
|
|
|
|
CollapsingHeader::new("Fonts")
|
|
|
|
// .default_open()
|
|
|
|
.show(ui, |ui| {
|
|
|
|
let old_font_definitions = self.fonts().definitions();
|
|
|
|
let mut new_font_definitions = old_font_definitions.clone();
|
|
|
|
font_definitions_ui(&mut new_font_definitions, ui);
|
|
|
|
self.fonts().texture().ui(ui);
|
|
|
|
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:25:28 +00:00
|
|
|
|
2020-05-10 17:04:10 +00:00
|
|
|
pub fn inspection_ui(&self, ui: &mut Ui) {
|
|
|
|
use crate::containers::*;
|
2020-05-08 20:25:28 +00:00
|
|
|
|
2020-05-08 20:42:31 +00:00
|
|
|
ui.collapsing("Input", |ui| {
|
2020-05-12 15:09:54 +00:00
|
|
|
CollapsingHeader::new("Raw Input").show(ui, |ui| ui.ctx().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
|
|
|
ui.add(label!("Painting:").text_style(TextStyle::Heading));
|
|
|
|
self.paint_stats.lock().ui(ui);
|
2020-05-08 20:25:28 +00:00
|
|
|
});
|
|
|
|
}
|
2020-05-10 17:04:10 +00:00
|
|
|
|
|
|
|
pub fn memory_ui(&self, ui: &mut crate::Ui) {
|
|
|
|
use crate::widgets::*;
|
|
|
|
|
|
|
|
if ui
|
|
|
|
.add(Button::new("Reset all"))
|
|
|
|
.tooltip_text("Reset all Emigui state")
|
|
|
|
.clicked
|
|
|
|
{
|
|
|
|
*self.memory() = Default::default();
|
|
|
|
}
|
|
|
|
|
|
|
|
ui.horizontal(|ui| {
|
|
|
|
ui.add(label!(
|
|
|
|
"{} areas (window positions)",
|
|
|
|
self.memory().areas.count()
|
|
|
|
));
|
|
|
|
if ui.add(Button::new("Reset")).clicked {
|
|
|
|
self.memory().areas = Default::default();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
ui.horizontal(|ui| {
|
|
|
|
ui.add(label!(
|
|
|
|
"{} collapsing headers",
|
|
|
|
self.memory().collapsing_headers.len()
|
|
|
|
));
|
|
|
|
if ui.add(Button::new("Reset")).clicked {
|
|
|
|
self.memory().collapsing_headers = Default::default();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
ui.horizontal(|ui| {
|
|
|
|
ui.add(label!("{} menu bars", self.memory().menu_bar.len()));
|
|
|
|
if ui.add(Button::new("Reset")).clicked {
|
|
|
|
self.memory().menu_bar = Default::default();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
ui.horizontal(|ui| {
|
|
|
|
ui.add(label!("{} scroll areas", self.memory().scroll_areas.len()));
|
|
|
|
if ui.add(Button::new("Reset")).clicked {
|
|
|
|
self.memory().scroll_areas = Default::default();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
ui.horizontal(|ui| {
|
|
|
|
ui.add(label!("{} resize areas", self.memory().resize.len()));
|
|
|
|
if ui.add(Button::new("Reset")).clicked {
|
|
|
|
self.memory().resize = Default::default();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
ui.add(
|
|
|
|
label!("NOTE: the position of this window cannot be reset from within itself.")
|
|
|
|
.auto_shrink(),
|
|
|
|
);
|
|
|
|
}
|
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
|
|
|
|
2020-05-19 18:54:02 +00:00
|
|
|
impl mesher::PaintOptions {
|
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
|
|
|
}
|
|
|
|
}
|