use rin::events::*;
use std::rc::Rc;
use rin::color::*;
use rin::color::consts::*;
use rin::graphics::{Ttf, Vertex2DColor, Vertex2DTex, vertex2dcolor, CoordinateOrigin};
use rin::math::*;
use rin::window::{self, Events, MouseEvents};
use traits::*;
use std::str::FromStr;
use std::string::ToString;
use std::fmt::Debug;
use control_shape::ControlShape;
use utils;
pub struct TextBox<T: FromStr + ToString + Clone + Debug + 'static>{
name: String,
numeric: bool,
stream: StreamRc<'static, T>,
rendered: Sender<'static, ()>,
font: Rc<Ttf>,
color: Rgb<f32>,
non_attended: StreamRc<'static, window::Event>,
geometry: IterParameter<'static, Vec<Vertex2DColor>>,
text_geometry: IterParameter<'static, Vec<Vertex2DTex>>,
width: Property<'static, f32>,
height: Property<'static, f32>,
position: Property<'static, Pnt2>,
}
pub struct TextBoxBuilder<T: FromStr + ToString + Clone + Debug + 'static>{
name: String,
numeric: bool,
parameter: Property<'static, T>,
events: StreamRc<'static, window::Event>,
shape: ControlShape,
}
fn rect_vertices(rect: Rect<f32>, color: &Rgba<f32>) -> Vec<Vertex2DColor>{
let x = rect.pos.x;
let y = rect.pos.y;
let w = rect.width;
let h = rect.height;
vec![
vertex2dcolor(vec2(x,y), color),
vertex2dcolor(vec2(x+w,y), color),
vertex2dcolor(vec2(x+w,y+h), color),
vertex2dcolor(vec2(x+w,y+h), color),
vertex2dcolor(vec2(x,y+h), color),
vertex2dcolor(vec2(x,y), color),
]
}
fn geometry(rect: Rect<f64>, color: Rgb<f32>, alpha: f32) -> Vec<Vertex2DColor>{
let x = rect.pos.x as f32;
let y = rect.pos.y as f32;
let w = rect.width as f32;
let h = rect.height as f32;
let border_color = &rgba!(color, alpha);
let lw = 2.;
rect_vertices(Rect{pos: pnt2(x,y), width: w, height: lw}, border_color).into_iter()
.chain(rect_vertices(Rect{pos: pnt2(x,y+lw), width: lw, height: h-lw*2.}, border_color).into_iter())
.chain(rect_vertices(Rect{pos: pnt2(x+w,y+lw), width: -lw, height: h-lw*2.}, border_color).into_iter())
.chain(rect_vertices(Rect{pos: pnt2(x,y+h), width: w, height: -lw}, border_color).into_iter())
.collect()
}
fn geometry_cursor(rect: Rect<f64>, color: Rgb<f32>, alpha: f32, elapsed: f64, cursor_pos: f32, cursor_height: f32, selection_start: Option<f32>, margin: f32) -> Vec<Vertex2DColor>{
let x = cursor_pos;
let h = cursor_height;
let y = rect.pos.y as f32 + (rect.height as f32 - h) * 0.5;
let cursor_color = &rgba!(color, 0.5);
let selection_color = &rgba!(color, 0.3);
let lw = 2.;
let mut textbox = geometry(rect, color, alpha);
if (elapsed * 1.8) as u32 % 2 == 0 {
textbox.extend(rect_vertices(Rect{pos: pnt2(x,y), width: lw, height: h}, cursor_color));
}
if selection_start.is_some(){
let w = selection_start.unwrap().min(rect.max_x() as f32 - margin) - x;
textbox.extend(rect_vertices(Rect{pos: pnt2(x,y), width: w, height: h}, selection_color));
}
textbox
}
impl<T: FromStr + ToString + PartialEq + Clone + Debug> TextBoxBuilder<T>{
pub fn new<S: StreamT<'static, window::Event>>(name: &str, parameter: Property<'static, T>, events: S) -> TextBoxBuilder<T>{
let pos = Property::new(origin());
let width = Property::new(200.);
let height = Property::new(0.);
let margin = 0.;
TextBoxBuilder{
name: name.to_owned(),
numeric: true,
parameter,
events: events.rc(),
shape: ControlShape::new(pos, width, height, margin, margin, WHITE),
}
}
pub fn numeric(mut self, numeric: bool) -> TextBoxBuilder<T>{
self.numeric = numeric;
self
}
pub fn create(mut self) -> TextBox<T>{
let (rendered_sender, rendered_stream) = utils::rendered_stream(self.events.clone().unique());
let rendered = rendered_stream.to_property(false);
let font = self.shape.font().unwrap_or_else(|| ::utils::default_font());
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
enum State{
Focused,
NonFocused,
Cancelled,
}
let width = self.shape.width();
let mut height = self.shape.height();
let position = self.shape.position();
let rect: Property<'_, Rect<f64>> = position.clone()
.zip(width.clone())
.zip(height.clone())
.flatten()
.map(|(pos, width, height)| convert(Rect{pos, width, height}));
if *height == 0.{
height.set((font.line_height() as f64 * 1.8) as u32 as f32 );
}
let margin = (rect.height as f32 - font.line_height() as f32) / 2.;
let over = self.events.clone()
.mouse()
.moved()
.to_property(pnt2(0., 0.))
.zip(rect.clone())
.map(move |(pos, rect)| pos.inside(&rect));
let state = {
let over = over.clone();
self.events.clone()
.fold(State::NonFocused, move |state, event|{
match event{
window::Event::MousePressed{ button: window::MouseButton::Left, .. } =>
if *over { State::Focused } else { State::NonFocused },
window::Event::KeyPressed{ key: window::Key::Escape, .. } =>
State::Cancelled,
window::Event::KeyPressed{ key: window::Key::Enter, .. } =>
State::NonFocused,
_ => state
}
})
.rc()
};
let is_focused = state.clone()
.map(|state| state == State::Focused)
.to_property(false);
let mut visible_start = Property::new(0);
let cursor_pos;
let selection_start;
let text_stream;
{
fn cursor_pos_from_mouse(text: &str, font: &Rc<Ttf>, rect: &Rect<f64>, margin: f32, mouse: &Pnt2<f64>) -> usize{
font.shape_iter(text, rect.pos.x as f32 + margin, rect.pos.y as f32, CoordinateOrigin::TopLeft)
.take_while(|shape|{
shape.rect.pos.x + shape.rect.width / 2. < mouse.x as f32 &&
shape.rect.max_x() < rect.max_x() as f32 - margin
})
.count()
}
let parameter = self.parameter.clone();
let font = font.clone();
let rect = rect.clone();
let is_mouse_pressed = self.events.clone()
.mouse()
.is_pressed(window::MouseButton::Left)
.to_property(false);
let mouse_pos = self.events.clone()
.mouse()
.moved()
.to_property(pnt2!(0.));
let visible_start = visible_start.clone();
let is_mouse_pressed2 = is_mouse_pressed.clone();
let x_width = font.bounding_box("x", 0., 0., CoordinateOrigin::TopLeft).width as f64;
let cursor_selection_text = self.events.clone()
.filter_map(move |e| match e {
window::Event::Update{..} => if *is_mouse_pressed2{
Some((e, *rect))
}else{
None
},
_ => Some((e, *rect)),
})
.filter_by(is_focused.clone())
.fold((parameter.to_string().len(), 0., None, parameter.to_value()), move |(cursor_pos, mut cursor_offset, selection_start, value): (usize, f64, Option<usize>, T), (event, rect)|{
let mut clipboard_advance = 0;
let mut text = value.to_string();
let idx_start = selection_start.map(|s|{
text.char_indices().nth(s)
.map(|(idx,_c)| idx)
.unwrap_or(text.len())
});
let (start, end) = if let Some(idx_start) = idx_start{
let idx = text.char_indices().nth(cursor_pos)
.map(|(idx,_c)| idx)
.unwrap_or(text.len());
let start = ::std::cmp::min(idx, idx_start);
let end = ::std::cmp::max(idx, idx_start);
(start, end)
}else{
(cursor_pos, cursor_pos)
};
let text = match event{
window::Event::KeyPressed{key: window::Key::Backspace, ..} => {
if !text.is_empty() && cursor_pos > 0{
if idx_start.is_some(){
text.drain(start .. end);
}else{
let idx = text.char_indices().nth(cursor_pos - 1).unwrap().0;
text.remove(idx);
}
}
text
}
window::Event::KeyPressed{key: window::Key::Delete, .. } => {
if !text.is_empty() && cursor_pos < text.char_indices().count(){
if idx_start.is_some(){
text.drain(start .. end);
}else{
let idx = text.char_indices().nth(cursor_pos).unwrap().0;
text.remove(idx);
}
}
text
}
window::Event::KeyPressed{key: window::Key::Codepoint('c'), mods: window::KeyModifiers::CONTROL, .. } |
window::Event::KeyPressed{key: window::Key::Codepoint('C'), mods: window::KeyModifiers::CONTROL, .. } => {
if !text.is_empty() {
if idx_start.is_some(){
window::set_clipboard_string(&text[start .. end]);
}
}
text
}
window::Event::KeyPressed{key: window::Key::Codepoint('x'), mods: window::KeyModifiers::CONTROL, .. } |
window::Event::KeyPressed{key: window::Key::Codepoint('X'), mods: window::KeyModifiers::CONTROL, .. } => {
if !text.is_empty() {
let idx_start = (selection_start).map(|s|{
text.char_indices().nth(s)
.map(|(idx,_c)| idx)
.unwrap_or(text.len())
});
if idx_start.is_some(){
window::set_clipboard_string(&text[start .. end]);
text.drain(start .. end);
}
}
text
}
window::Event::KeyPressed{key: window::Key::Codepoint('v'), mods: window::KeyModifiers::CONTROL, .. } |
window::Event::KeyPressed{key: window::Key::Codepoint('V'), mods: window::KeyModifiers::CONTROL, .. } => {
let idx_start = (selection_start).map(|s|{
text.char_indices().nth(s)
.map(|(idx,_c)| idx)
.unwrap_or(text.len())
});
let clip = window::clipboard_string();
clipboard_advance = clip.chars().count();
if idx_start.is_some(){
text[..start].to_owned() + &clip + &text[end..]
}else{
text[..cursor_pos].to_owned() + &clip + &text[cursor_pos..]
}
}
window::Event::Char{character} => {
let idx = text.char_indices().nth(cursor_pos)
.map(|(idx,_c)| idx)
.unwrap_or(text.len());
if idx_start.is_some(){
text.drain(start .. end);
}
text.insert(idx, character);
text
}
_ => text,
};
let text_len = text.char_indices().count();
let prev_cursor_pos = cursor_pos;
let visible_text = text.chars().skip(*visible_start).collect::<String>();
let cursor_pos = match event{
window::Event::KeyPressed{key: window::Key::Left, mods, ..} =>
if mods.is_empty() || mods == window::KeyModifiers::SHIFT {
if cursor_pos > 0 { cursor_pos - 1 } else { cursor_pos }
}else if mods.contains(window::KeyModifiers::CONTROL){
text_len
}else{
cursor_pos
},
window::Event::KeyPressed{key: window::Key::Right, mods, ..} =>
if mods.is_empty() || mods == window::KeyModifiers::SHIFT {
if cursor_pos < text.len() { cursor_pos + 1 } else { cursor_pos }
}else if mods.contains(window::KeyModifiers::CONTROL){
0
}else{
cursor_pos
},
window::Event::KeyPressed{key: window::Key::Home, ..} => 0,
window::Event::KeyPressed{key: window::Key::End, ..} => text_len,
window::Event::KeyPressed{key: window::Key::Backspace, ..} =>
if selection_start.is_none() && cursor_pos > 0 {
cursor_pos - 1
} else {
cursor_pos
},
window::Event::Char{..} =>
cursor_pos + 1,
window::Event::MousePressed{pos, button: window::MouseButton::Left, ..} =>
cursor_pos_from_mouse(&visible_text, &font, &rect, margin, &pos) + *visible_start,
window::Event::MouseReleased{button: window::MouseButton::Left, ..} => {
cursor_offset = 0.;
cursor_pos
}
window::Event::MouseMoved{pos} =>
if *is_mouse_pressed{
cursor_pos_from_mouse(&visible_text, &font, &rect, margin, &pos) + *visible_start
}else{
cursor_pos
},
window::Event::KeyPressed{key: window::Key::Codepoint('v'), mods: window::KeyModifiers::CONTROL, .. } |
window::Event::KeyPressed{key: window::Key::Codepoint('V'), mods: window::KeyModifiers::CONTROL, .. } =>
min(cursor_pos, selection_start.unwrap_or(cursor_pos)) + clipboard_advance,
window::Event::KeyPressed{key: window::Key::Codepoint('x'), mods: window::KeyModifiers::CONTROL, .. } |
window::Event::KeyPressed{key: window::Key::Codepoint('X'), mods: window::KeyModifiers::CONTROL, .. } =>
min(cursor_pos, selection_start.unwrap_or(cursor_pos)),
window::Event::Update{ delta } => {
if mouse_pos.x < rect.pos.x + margin as f64{
let speed = ((rect.pos.x + margin as f64 - mouse_pos.x) / x_width + 1.) * 3.;
cursor_offset -= delta * speed;
}else if mouse_pos.x > rect.max_x() - margin as f64{
let speed = ((mouse_pos.x - rect.max_x() + margin as f64) / x_width + 1.) * 3.;
cursor_offset += delta * speed;
}else{
cursor_offset = 0.;
}
if cursor_offset.abs() >= 1. {
let i_cursor_offset = cursor_offset as i32;
cursor_offset -= i_cursor_offset as f64;
clamp(cursor_pos as i32 + i_cursor_offset, 0, text.len() as i32) as usize
}else{
cursor_pos
}
}
_ => clamp(cursor_pos, 0, text.len())
};
let selection_start = match event{
window::Event::KeyPressed{key: window::Key::Left, mods, ..} |
window::Event::KeyPressed{key: window::Key::Right, mods, ..} |
window::Event::KeyPressed{key: window::Key::Home, mods, ..} |
window::Event::KeyPressed{key: window::Key::End, mods, ..} =>
if mods.contains(window::KeyModifiers::SHIFT){
if selection_start.is_some(){ selection_start } else { Some(prev_cursor_pos) }
}else{
None
},
window::Event::KeyPressed{key: window::Key::Backspace, ..} |
window::Event::Char{..} |
window::Event::KeyPressed{key: window::Key::Codepoint('v'), mods: window::KeyModifiers::CONTROL, .. } |
window::Event::KeyPressed{key: window::Key::Codepoint('V'), mods: window::KeyModifiers::CONTROL, .. } |
window::Event::KeyPressed{key: window::Key::Codepoint('x'), mods: window::KeyModifiers::CONTROL, .. } |
window::Event::KeyPressed{key: window::Key::Codepoint('X'), mods: window::KeyModifiers::CONTROL, .. } => None,
window::Event::MousePressed{button: window::MouseButton::Left, ..} =>
None,
window::Event::MouseReleased{button: window::MouseButton::Left, ..} =>
selection_start,
window::Event::MouseMoved{..} =>
if *is_mouse_pressed && selection_start.is_none(){
Some(prev_cursor_pos)
}else{
selection_start
},
_ => selection_start.map(|s| clamp(s, 0, text.len()))
};
let value = T::from_str(&text).unwrap_or_else(|_| value);
(cursor_pos, cursor_offset, selection_start, value)
}).rc();
cursor_pos = cursor_selection_text.clone()
.map(|(cursor_pos, _, _selection_start, _text)| cursor_pos)
.dedup()
.rc();
selection_start = cursor_selection_text.clone()
.map(|(_cursor_pos, _, selection_start, _text)| selection_start)
.dedup()
.to_property(None);
text_stream = cursor_selection_text
.map(|(_cursor_pos, _, _selection_start, text)| text)
.dedup()
.rc();
}
let focused_text_parameter = text_stream.clone()
.to_property(self.parameter.to_value());
let non_attended = self.events.clone()
.filter_by(!over.clone());
let filtered_events = self.events.clone()
.filter_by(rendered)
.update()
.rc();
let visible_start_stream;
let text_geometry;
{
let font = font.clone();
let parameter = focused_text_parameter.clone();
let statep = state.clone().to_property(State::NonFocused);
let state = state.clone();
let rect = rect.clone();
let cursor_pos_p = cursor_pos.clone()
.to_property(parameter.to_string().len());
let visible_start_text_geometry = text_stream.clone().to_bool()
.merge(state.to_bool())
.merge(cursor_pos.clone().to_bool())
.scan(0, move |visible_start, _|{
let x = rect.pos.x as f32 + margin.round();
let y = rect.pos.y as f32 + rect.height as f32 - margin.round() + font.descender();
let text = parameter.to_string();
let mesh = match *statep{
State::Focused => {
if cursor_pos_p.to_value() < *visible_start{
*visible_start = *cursor_pos_p;
}
let mut slice: String = text.chars()
.skip(*visible_start)
.take(*cursor_pos_p - *visible_start)
.collect();
let mut max_x = font.bounding_box(&slice, x, y, CoordinateOrigin::TopLeft).max_x();
if max_x > rect.max_x() as f32 - margin {
while max_x > rect.max_x() as f32 - margin{
slice = slice.chars().skip(1).collect();
*visible_start += 1;
max_x = font.bounding_box(&slice, x, y, CoordinateOrigin::TopLeft).max_x();
}
font.mesh(&slice, x, y, CoordinateOrigin::TopLeft).into()
}else{
let slice: String = text.chars().skip(*visible_start).collect();
let max_x = font.bounding_box(&slice, x, y, CoordinateOrigin::TopLeft).max_x();
if max_x > rect.max_x() as f32 - margin{
let slice: String = text.chars().skip(*visible_start).collect();
let slice: String = font.shape_iter(&slice, x, y, CoordinateOrigin::TopLeft)
.take_while(|shape| shape.rect.max_x() < rect.max_x() as f32 - margin)
.map(|shape| shape.character)
.collect();
font.mesh(&slice, x, y, CoordinateOrigin::TopLeft).into()
}else{
font.mesh(&slice, x, y, CoordinateOrigin::TopLeft).into()
}
}
}
State::NonFocused | State::Cancelled=> {
let max_x = font.bounding_box(&text, x, y, CoordinateOrigin::TopLeft).max_x();
if max_x > rect.max_x() as f32 - margin{
let slice: String = font.shape_iter(&text, x, y, CoordinateOrigin::TopLeft)
.take_while(|shape| shape.rect.max_x() < rect.max_x() as f32 - margin)
.map(|shape| shape.character)
.collect();
font.mesh(&slice, x, y, CoordinateOrigin::TopLeft).into()
}else{
font.mesh(&text, x, y, CoordinateOrigin::TopLeft).into()
}
}
};
Some((*visible_start, mesh))
}).rc();
visible_start_stream = visible_start_text_geometry.clone().map(|(v,_t)| v)
.connect_to_property(&mut visible_start);
text_geometry = visible_start_text_geometry.map(|(_v,t)| t);
};
let text_geometry = IterParameter::new(text_geometry.iter_async());
let visible_start = visible_start_stream.to_property(0);
let geometry = {
let color = self.shape.color();
let parameter = focused_text_parameter.clone();
let font = font.clone();
let state = state.clone().to_property(State::NonFocused);
let rect = rect.clone();
let cursor_pos = cursor_pos
.to_property(self.parameter.to_string().len());
filtered_events.clone()
.scan(0., move |elapsed, delta|{
*elapsed += delta;
let x = rect.pos.x as f32 + margin.round();
let y = rect.pos.y as f32 + rect.height as f32 - margin.round() + font.descender();
let text = parameter.to_string();
match *state{
State::Focused => {
let cursor_pos = if *cursor_pos > *visible_start {
let idx_start = text.char_indices().nth(*visible_start)
.map(|(idx, _c)| idx)
.unwrap_or(text.len());
let idx_end = text.char_indices()
.skip(*visible_start)
.nth(*cursor_pos - *visible_start)
.map(|(idx, _c)| idx)
.unwrap_or(text.len());
let slice = &text[idx_start..idx_end];
font.bounding_box(slice, x, y, CoordinateOrigin::TopLeft).max_x()
}else{
rect.pos.x as f32 + margin
};
let selection_start = (*selection_start).map(|selection_start|
if selection_start > *visible_start {
let idx_start = text.char_indices().nth(*visible_start)
.map(|(idx, _c)| idx)
.unwrap_or(text.len());
let idx_end = text.char_indices()
.skip(*visible_start)
.nth(selection_start - *visible_start)
.map(|(idx, _c)| idx)
.unwrap_or(text.len());
let slice = &text[idx_start..idx_end];
font.bounding_box(slice, x, y, CoordinateOrigin::TopLeft).max_x()
}else{
rect.pos.x as f32 + margin
}
);
Some(geometry_cursor(*rect, color, 0.1, *elapsed, cursor_pos,
font.line_height(), selection_start, margin))
}
State::NonFocused | State::Cancelled => Some(geometry(*rect, color, 0.1)),
}
}).iter_async()
};
let geometry = IterParameter::new(geometry);
let stream = state
.filter(|s| *s == State::NonFocused)
.map(move |_| focused_text_parameter.to_value())
.dedup()
.connect_to_property(&mut self.parameter)
.rc();
TextBox{
name: self.name.clone(),
numeric: self.numeric,
stream,
rendered: rendered_sender,
font,
color: self.shape.color(),
non_attended: non_attended.rc(),
geometry,
text_geometry,
width,
height,
position,
}
}
}
impl<T: FromStr + ToString + PartialEq + Clone + Debug + 'static> ControlBuilder for TextBoxBuilder<T> {
type Control = TextBox<T>;
fn shape(&mut self) -> &mut ControlShape {
&mut self.shape
}
fn create(self) -> TextBox<T>{
self.create()
}
}
impl<T:FromStr + ToString + PartialEq + Clone + Debug + 'static> ControlBuilt<Property<'static, T>> for TextBox<T>{
type Builder = TextBoxBuilder<T>;
}
impl ControlDefaultProperty for Property<'static, String>{
type Builder = TextBoxBuilder<String>;
}
impl<T:FromStr + ToString + PartialEq + Clone + Debug + 'static> BuilderFromProperty<Property<'static, T>> for TextBoxBuilder<T>{
fn from_parameter<S: StreamT<'static, window::Event>>(parameter: Property<'static, T>, name: &str, events: S) -> TextBoxBuilder<T>{
TextBoxBuilder::new(name, parameter, events)
}
}
impl<T:FromStr + ToString + Clone + Debug + 'static> TextBox<T>{
pub fn stream(&self) -> StreamRc<'static, T>{
self.stream.clone()
}
}
impl<T:FromStr + ToString + Clone + Debug + 'static> ControlGeometry for TextBox<T>{
fn flexible() -> bool{
true
}
fn geometry(&self, _container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DColor>>{
self.rendered.send(());
Some(self.geometry.to_value().unwrap_or(vec![]))
}
fn text_geometry(&self, _container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DTex>>{
Some(self.text_geometry.to_value().unwrap_or(vec![]))
}
fn texture_geometry(&self, _container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DTex>>{
None
}
fn font(&self) -> Option<&Ttf>{
Some(&self.font)
}
fn color(&self) -> &Rgb<f32>{
&self.color
}
fn position(&self) -> &Property<'static, Pnt2>{
&self.position
}
fn width(&self) -> &Property<'static, f32>{
&self.width
}
fn height(&self) -> &Property<'static, f32>{
&self.height
}
}
impl<T:FromStr + ToString + Clone + Debug + 'static> ControlEvents for TextBox<T>{
fn non_attended(&self) -> StreamRc<'static, window::Event>{
self.non_attended.clone()
}
}
impl<T:FromStr + ToString + Clone + Debug + 'static> Control for TextBox<T>{
fn name(&self) -> &str{
&self.name
}
}
#[cfg(any(feature = "gl", feature = "gles", feature="webgl"))]
mod gl{
use rin::gl::{self, Renderer2d};
use rin::graphics::*;
use std::str::FromStr;
use std::fmt::Debug;
use traits::ControlGeometry;
use utils;
use rin::gl::RenderSurface;
use rin::math::{convert, Rect};
impl<T:FromStr + ToString + Clone + Debug + 'static> gl::Render3d for super::TextBox<T>{
fn render<R: RenderSurface>(&self, gl: &gl::Renderer<R>){
let viewport = *gl.context().state().viewport();
let viewport: Rect<i32> = viewport.into();
let viewport: Rect<f32> = convert(viewport);
let viewport = Some(&viewport);
if let Some(geometry) = self.geometry(viewport).map(|g| mesh(g, PrimitiveType::Triangles)){
let gl = gl.with_properties(&[
gl::Property::Blend(true),
gl::Property::BlendEquation(gl::FUNC_ADD),
gl::Property::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA),
]);
gl.draw_mesh(&geometry);
}
if let (Some(text_geometry), Some(font)) = (self.text_geometry(viewport), self.font()){
let font_material = utils::gl::font_material(&gl, font, self.color());
let text_geometry = mesh(text_geometry, PrimitiveType::Triangles);
gl.draw_mesh_with_material(&text_geometry, font_material);
}
}
}
}