Rename Data to Context and move to own file

This commit is contained in:
Emil Ernerfeldt 2020-04-17 15:33:52 +02:00
parent de76cb6190
commit f709423809
8 changed files with 159 additions and 158 deletions

104
emigui/src/context.rs Normal file
View file

@ -0,0 +1,104 @@
use std::sync::{Arc, Mutex};
use crate::*;
/// Contains the input, style and output of all GUI commands.
pub struct Context {
/// The default style for new regions
pub(crate) style: Mutex<Style>,
pub(crate) fonts: Arc<Fonts>,
pub(crate) input: GuiInput,
pub(crate) memory: Mutex<Memory>,
pub(crate) graphics: Mutex<GraphicLayers>,
}
impl Clone for Context {
fn clone(&self) -> Self {
Context {
style: Mutex::new(self.style()),
fonts: self.fonts.clone(),
input: self.input,
memory: Mutex::new(self.memory.lock().unwrap().clone()),
graphics: Mutex::new(self.graphics.lock().unwrap().clone()),
}
}
}
impl Context {
pub fn new(pixels_per_point: f32) -> Context {
Context {
style: Default::default(),
fonts: Arc::new(Fonts::new(pixels_per_point)),
input: Default::default(),
memory: Default::default(),
graphics: Default::default(),
}
}
pub fn input(&self) -> &GuiInput {
&self.input
}
pub fn style(&self) -> Style {
*self.style.lock().unwrap()
}
pub fn set_style(&self, style: Style) {
*self.style.lock().unwrap() = style;
}
// TODO: move
pub fn new_frame(&mut self, gui_input: GuiInput) {
self.input = gui_input;
if !gui_input.mouse_down || gui_input.mouse_pos.is_none() {
self.memory.lock().unwrap().active_id = None;
}
}
/// Is the user interacting with anything?
pub fn any_active(&self) -> bool {
self.memory.lock().unwrap().active_id.is_some()
}
pub fn interact(&self, layer: Layer, rect: Rect, interaction_id: Option<Id>) -> InteractInfo {
let mut memory = self.memory.lock().unwrap();
let hovered = if let Some(mouse_pos) = self.input.mouse_pos {
if rect.contains(mouse_pos) {
let is_something_else_active =
memory.active_id.is_some() && memory.active_id != interaction_id;
!is_something_else_active && layer == memory.layer_at(mouse_pos)
} else {
false
}
} else {
false
};
let active = if interaction_id.is_some() {
if hovered && self.input.mouse_clicked {
memory.active_id = interaction_id;
}
memory.active_id == interaction_id
} else {
false
};
let clicked = hovered && self.input.mouse_released;
InteractInfo {
rect,
hovered,
clicked,
active,
}
}
}
impl Context {
pub fn style_ui(&self, region: &mut Region) {
let mut style = self.style();
style.ui(region);
self.set_style(style);
}
}

View file

@ -11,7 +11,7 @@ struct Stats {
/// Encapsulates input, layout and painting for ease of use.
pub struct Emigui {
pub last_input: RawInput,
pub data: Arc<layout::Data>,
pub ctx: Arc<Context>,
stats: Stats,
anti_alias: bool,
}
@ -20,14 +20,14 @@ impl Emigui {
pub fn new(pixels_per_point: f32) -> Emigui {
Emigui {
last_input: Default::default(),
data: Arc::new(layout::Data::new(pixels_per_point)),
ctx: Arc::new(Context::new(pixels_per_point)),
stats: Default::default(),
anti_alias: true,
}
}
pub fn texture(&self) -> &Texture {
self.data.fonts.texture()
self.ctx.fonts.texture()
}
pub fn new_frame(&mut self, new_input: RawInput) {
@ -35,31 +35,31 @@ impl Emigui {
self.last_input = new_input;
// TODO: avoid this clone
let mut new_data = (*self.data).clone();
let mut new_data = (*self.ctx).clone();
new_data.new_frame(gui_input);
self.data = Arc::new(new_data);
self.ctx = Arc::new(new_data);
}
pub fn whole_screen_region(&mut self) -> Region {
Region {
data: self.data.clone(),
ctx: self.ctx.clone(),
layer: Layer::Background,
style: self.data.style(),
style: self.ctx.style(),
id: Default::default(),
dir: layout::Direction::Vertical,
align: layout::Align::Center,
cursor: Default::default(),
bounding_size: Default::default(),
available_space: self.data.input.screen_size,
available_space: self.ctx.input.screen_size,
}
}
pub fn paint(&mut self) -> Mesh {
let paint_commands: Vec<PaintCmd> = self.data.graphics.lock().unwrap().drain().collect();
let paint_commands: Vec<PaintCmd> = self.ctx.graphics.lock().unwrap().drain().collect();
let mut mesher = Mesher::new(self.last_input.pixels_per_point);
mesher.anti_alias = self.anti_alias;
mesher.paint(&self.data.fonts, &paint_commands);
mesher.paint(&self.ctx.fonts, &paint_commands);
let mesh = mesher.mesh;
self.stats.num_vertices = mesh.vertices.len();
self.stats.num_triangles = mesh.indices.len() / 3;
@ -69,20 +69,20 @@ impl Emigui {
pub fn ui(&mut self, region: &mut Region) {
region.foldable("Style", |region| {
region.add(Checkbox::new(&mut self.anti_alias, "Antialias"));
self.data.style_ui(region);
self.ctx.style_ui(region);
});
region.foldable("Fonts", |region| {
let old_font_definitions = self.data.fonts.definitions();
let old_font_definitions = self.ctx.fonts.definitions();
let mut new_font_definitions = old_font_definitions.clone();
font_definitions_ui(&mut new_font_definitions, region);
self.data.fonts.texture().ui(region);
self.ctx.fonts.texture().ui(region);
if *old_font_definitions != new_font_definitions {
let mut new_data = (*self.data).clone();
let mut new_data = (*self.ctx).clone();
let fonts =
Fonts::from_definitions(new_font_definitions, self.data.input.pixels_per_point);
Fonts::from_definitions(new_font_definitions, self.ctx.input.pixels_per_point);
new_data.fonts = Arc::new(fonts);
self.data = Arc::new(new_data);
self.ctx = Arc::new(new_data);
}
});

View file

@ -1,7 +1,4 @@
use std::{
hash::Hash,
sync::{Arc, Mutex},
};
use std::{hash::Hash, sync::Arc};
use crate::{widgets::*, *};
@ -22,7 +19,7 @@ pub struct GuiResponse {
pub rect: Rect,
/// Used for showing a popup (if any)
pub data: Arc<Data>,
pub ctx: Arc<Context>,
}
impl GuiResponse {
@ -32,9 +29,9 @@ impl GuiResponse {
F: FnOnce(&mut Region),
{
if self.hovered {
if let Some(mouse_pos) = self.data.input().mouse_pos {
if let Some(mouse_pos) = self.ctx.input().mouse_pos {
let window_pos = mouse_pos + vec2(16.0, 16.0);
show_popup(&self.data, window_pos, add_contents);
show_popup(&self.ctx, window_pos, add_contents);
}
}
self
@ -95,121 +92,19 @@ pub fn make_id<H: Hash>(source: &H) -> Id {
// ----------------------------------------------------------------------------
// TODO: give a better name. Context?
/// Contains the input, style and output of all GUI commands.
pub struct Data {
/// The default style for new regions
pub(crate) style: Mutex<Style>,
pub(crate) fonts: Arc<Fonts>,
pub(crate) input: GuiInput,
pub(crate) memory: Mutex<Memory>,
pub(crate) graphics: Mutex<GraphicLayers>,
}
impl Clone for Data {
fn clone(&self) -> Self {
Data {
style: Mutex::new(self.style()),
fonts: self.fonts.clone(),
input: self.input,
memory: Mutex::new(self.memory.lock().unwrap().clone()),
graphics: Mutex::new(self.graphics.lock().unwrap().clone()),
}
}
}
impl Data {
pub fn new(pixels_per_point: f32) -> Data {
Data {
style: Default::default(),
fonts: Arc::new(Fonts::new(pixels_per_point)),
input: Default::default(),
memory: Default::default(),
graphics: Default::default(),
}
}
pub fn input(&self) -> &GuiInput {
&self.input
}
pub fn style(&self) -> Style {
*self.style.lock().unwrap()
}
pub fn set_style(&self, style: Style) {
*self.style.lock().unwrap() = style;
}
// TODO: move
pub fn new_frame(&mut self, gui_input: GuiInput) {
self.input = gui_input;
if !gui_input.mouse_down || gui_input.mouse_pos.is_none() {
self.memory.lock().unwrap().active_id = None;
}
}
/// Is the user interacting with anything?
pub fn any_active(&self) -> bool {
self.memory.lock().unwrap().active_id.is_some()
}
pub fn interact(&self, layer: Layer, rect: Rect, interaction_id: Option<Id>) -> InteractInfo {
let mut memory = self.memory.lock().unwrap();
let hovered = if let Some(mouse_pos) = self.input.mouse_pos {
if rect.contains(mouse_pos) {
let is_something_else_active =
memory.active_id.is_some() && memory.active_id != interaction_id;
!is_something_else_active && layer == memory.layer_at(mouse_pos)
} else {
false
}
} else {
false
};
let active = if interaction_id.is_some() {
if hovered && self.input.mouse_clicked {
memory.active_id = interaction_id;
}
memory.active_id == interaction_id
} else {
false
};
let clicked = hovered && self.input.mouse_released;
InteractInfo {
rect,
hovered,
clicked,
active,
}
}
}
impl Data {
pub fn style_ui(&self, region: &mut Region) {
let mut style = self.style();
style.ui(region);
self.set_style(style);
}
}
/// Show a pop-over window
pub fn show_popup<F>(data: &Arc<Data>, window_pos: Vec2, add_contents: F)
pub fn show_popup<F>(ctx: &Arc<Context>, window_pos: Vec2, add_contents: F)
where
F: FnOnce(&mut Region),
{
let layer = Layer::Popup;
let where_to_put_background = data.graphics.lock().unwrap().layer(layer).len();
let where_to_put_background = ctx.graphics.lock().unwrap().layer(layer).len();
let style = data.style();
let style = ctx.style();
let window_padding = style.window_padding;
let mut contents_region = Region {
data: data.clone(),
ctx: ctx.clone(),
layer: Layer::Popup,
style,
id: Default::default(),
@ -217,7 +112,7 @@ where
align: Align::Min,
cursor: window_pos + window_padding,
bounding_size: vec2(0.0, 0.0),
available_space: vec2(data.input.screen_size.x.min(350.0), std::f32::INFINITY), // TODO: popup/tooltip width
available_space: vec2(ctx.input.screen_size.x.min(350.0), std::f32::INFINITY), // TODO: popup/tooltip width
};
add_contents(&mut contents_region);
@ -230,7 +125,7 @@ where
let rect = Rect::from_min_size(window_pos, outer_size);
let mut graphics = data.graphics.lock().unwrap();
let mut graphics = ctx.graphics.lock().unwrap();
let graphics = graphics.layer(layer);
graphics.insert(
where_to_put_background,

View file

@ -7,6 +7,7 @@ extern crate serde;
extern crate serde_derive;
pub mod color;
mod context;
mod emigui;
pub mod example_app;
mod font;
@ -26,6 +27,7 @@ mod window;
pub use {
crate::emigui::Emigui,
color::Color,
context::Context,
fonts::{FontDefinitions, Fonts, TextStyle},
layers::*,
layout::{Align, Id},

View file

@ -6,7 +6,7 @@ use crate::{font::TextFragment, layout::*, widgets::*, *};
/// with a type of layout (horizontal or vertical).
/// TODO: make Region a trait so we can have type-safe HorizontalRegion etc?
pub struct Region {
pub(crate) data: Arc<Data>,
pub(crate) ctx: Arc<Context>,
/// Where to put the graphics output of this Region
pub(crate) layer: Layer,
@ -39,7 +39,7 @@ impl Region {
/// Can be used for free painting.
/// NOTE: all coordinates are screen coordinates!
pub fn add_paint_cmd(&mut self, paint_cmd: PaintCmd) {
self.data
self.ctx
.graphics
.lock()
.unwrap()
@ -48,7 +48,7 @@ impl Region {
}
pub fn add_paint_cmds(&mut self, mut cmds: Vec<PaintCmd>) {
self.data
self.ctx
.graphics
.lock()
.unwrap()
@ -61,16 +61,16 @@ impl Region {
&self.style
}
pub fn data(&self) -> &Arc<Data> {
&self.data
pub fn ctx(&self) -> &Arc<Context> {
&self.ctx
}
pub fn input(&self) -> &GuiInput {
self.data.input()
self.ctx.input()
}
pub fn fonts(&self) -> &Fonts {
&*self.data.fonts
&*self.ctx.fonts
}
pub fn width(&self) -> f32 {
@ -124,7 +124,7 @@ impl Region {
);
let open = {
let mut memory = self.data.memory.lock().unwrap();
let mut memory = self.ctx.memory.lock().unwrap();
if interact.clicked {
if memory.open_foldables.contains(&id) {
memory.open_foldables.remove(&id);
@ -194,7 +194,7 @@ impl Region {
{
let indent = vec2(self.style.indent, 0.0);
let mut child_region = Region {
data: self.data.clone(),
ctx: self.ctx.clone(),
layer: self.layer,
style: self.style,
id: self.id,
@ -212,7 +212,7 @@ impl Region {
/// Return a sub-region relative to the parent
pub fn relative_region(&mut self, rect: Rect) -> Region {
Region {
data: self.data.clone(),
ctx: self.ctx.clone(),
layer: self.layer,
style: self.style,
id: self.id,
@ -254,7 +254,7 @@ impl Region {
F: FnOnce(&mut Region),
{
let mut child_region = Region {
data: self.data.clone(),
ctx: self.ctx.clone(),
layer: self.layer,
style: self.style,
id: self.id,
@ -302,7 +302,7 @@ impl Region {
let mut columns: Vec<Region> = (0..num_columns)
.map(|col_idx| Region {
data: self.data.clone(),
ctx: self.ctx.clone(),
layer: self.layer,
style: self.style,
id: self.make_child_id(&("column", col_idx)),
@ -337,7 +337,7 @@ impl Region {
pub fn reserve_space(&mut self, size: Vec2, interaction_id: Option<Id>) -> InteractInfo {
let pos = self.reserve_space_without_padding(size + self.style.item_spacing);
let rect = Rect::from_min_size(pos, size);
self.data.interact(self.layer, rect, interaction_id)
self.ctx.interact(self.layer, rect, interaction_id)
}
/// Reserve this much space and move the cursor.
@ -438,7 +438,7 @@ impl Region {
clicked: interact.clicked,
active: interact.active,
rect: interact.rect,
data: self.data.clone(),
ctx: self.ctx.clone(),
}
}
}

View file

@ -124,7 +124,7 @@ impl Texture {
if let Some(mouse_pos) = region.input().mouse_pos {
if interact.hovered {
show_popup(region.data(), mouse_pos, |region| {
show_popup(region.ctx(), mouse_pos, |region| {
let zoom_rect = region.reserve_space(vec2(128.0, 128.0), None).rect;
let u = remap_clamp(
mouse_pos.x,

View file

@ -1,7 +1,7 @@
use std::sync::Arc;
use crate::{
layout::{make_id, Data, Direction},
layout::{make_id, Direction},
widgets::Label,
*,
};
@ -24,13 +24,13 @@ impl Window {
}
}
pub fn show<F>(self, data: Arc<Data>, add_contents: F)
pub fn show<F>(self, ctx: &Arc<Context>, add_contents: F)
where
F: FnOnce(&mut Region),
{
let id = make_id(&self.title);
let mut state = data.memory.lock().unwrap().get_or_create_window(
let mut state = ctx.memory.lock().unwrap().get_or_create_window(
id,
Rect::from_min_size(
vec2(400.0, 200.0), // TODO
@ -39,13 +39,13 @@ impl Window {
);
let layer = Layer::Window(id);
let where_to_put_background = data.graphics.lock().unwrap().layer(layer).len();
let where_to_put_background = ctx.graphics.lock().unwrap().layer(layer).len();
let style = data.style();
let style = ctx.style();
let window_padding = style.window_padding;
let mut contents_region = Region {
data: data.clone(),
ctx: ctx.clone(),
layer: Layer::Popup,
style,
id: Default::default(),
@ -53,7 +53,7 @@ impl Window {
align: Align::Min,
cursor: state.rect.min() + window_padding,
bounding_size: vec2(0.0, 0.0),
available_space: vec2(data.input.screen_size.x.min(350.0), std::f32::INFINITY), // TODO: window.width
available_space: vec2(ctx.input.screen_size.x.min(350.0), std::f32::INFINITY), // TODO: window.width
};
// Show top bar:
@ -69,7 +69,7 @@ impl Window {
state.rect = Rect::from_min_size(state.rect.min(), outer_size);
let mut graphics = data.graphics.lock().unwrap();
let mut graphics = ctx.graphics.lock().unwrap();
let graphics = graphics.layer(layer);
graphics.insert(
where_to_put_background,
@ -84,12 +84,12 @@ impl Window {
},
);
let interact = data.interact(layer, state.rect, Some(id));
let interact = ctx.interact(layer, state.rect, Some(id));
if interact.active {
state.rect = state.rect.translate(data.input().mouse_move);
state.rect = state.rect.translate(ctx.input().mouse_move);
}
let mut memory = data.memory.lock().unwrap();
let mut memory = ctx.memory.lock().unwrap();
if interact.active || interact.clicked {
memory.move_window_to_top(id);
}

View file

@ -92,7 +92,7 @@ fn main() {
emigui.ui(&mut region);
// TODO: Make it simpler to show a window
Window::new("Test window").show(region.data().clone(), |region| {
Window::new("Test window").show(region.ctx(), |region| {
region.add(label!("Grab the window and move it around!"));
});