mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 13:50:04 -04:00
Rename from "Emigui" to "Egui"
Shorter to type (especially in code).
This commit is contained in:
232
egui_glium/src/lib.rs
Normal file
232
egui_glium/src/lib.rs
Normal file
@@ -0,0 +1,232 @@
|
||||
#![deny(warnings)]
|
||||
#![warn(clippy::all)]
|
||||
#![allow(clippy::single_match)]
|
||||
mod painter;
|
||||
|
||||
pub use painter::Painter;
|
||||
|
||||
use {
|
||||
clipboard::{ClipboardContext, ClipboardProvider},
|
||||
egui::*,
|
||||
glium::glutin::{self, VirtualKeyCode},
|
||||
};
|
||||
|
||||
pub fn init_clipboard() -> Option<ClipboardContext> {
|
||||
match ClipboardContext::new() {
|
||||
Ok(clipboard) => Some(clipboard),
|
||||
Err(err) => {
|
||||
eprintln!("Failed to initialize clipboard: {}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_event(
|
||||
event: glutin::Event,
|
||||
clipboard: Option<&mut ClipboardContext>,
|
||||
raw_input: &mut RawInput,
|
||||
running: &mut bool,
|
||||
) {
|
||||
use glutin::WindowEvent::*;
|
||||
match event {
|
||||
glutin::Event::WindowEvent { event, .. } => match event {
|
||||
CloseRequested | Destroyed => *running = false,
|
||||
|
||||
Resized(glutin::dpi::LogicalSize { width, height }) => {
|
||||
raw_input.screen_size = vec2(width as f32, height as f32);
|
||||
}
|
||||
MouseInput { state, .. } => {
|
||||
raw_input.mouse_down = state == glutin::ElementState::Pressed;
|
||||
}
|
||||
CursorMoved { position, .. } => {
|
||||
raw_input.mouse_pos = Some(pos2(position.x as f32, position.y as f32));
|
||||
}
|
||||
CursorLeft { .. } => {
|
||||
raw_input.mouse_pos = None;
|
||||
}
|
||||
ReceivedCharacter(ch) => {
|
||||
if !should_ignore_char(ch) {
|
||||
if ch == '\r' {
|
||||
raw_input.events.push(Event::Text("\n".to_owned()));
|
||||
} else {
|
||||
raw_input.events.push(Event::Text(ch.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyboardInput { input, .. } => {
|
||||
if let Some(virtual_keycode) = input.virtual_keycode {
|
||||
// TODO: If mac
|
||||
if input.modifiers.logo && virtual_keycode == VirtualKeyCode::Q {
|
||||
*running = false;
|
||||
}
|
||||
|
||||
match virtual_keycode {
|
||||
VirtualKeyCode::Paste => {
|
||||
if let Some(clipboard) = clipboard {
|
||||
match clipboard.get_contents() {
|
||||
Ok(contents) => {
|
||||
raw_input.events.push(Event::Text(contents));
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Paste error: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VirtualKeyCode::Copy => raw_input.events.push(Event::Copy),
|
||||
VirtualKeyCode::Cut => raw_input.events.push(Event::Cut),
|
||||
_ => {
|
||||
if let Some(key) = translate_virtual_key_code(virtual_keycode) {
|
||||
raw_input.events.push(Event::Key {
|
||||
key,
|
||||
pressed: input.state == glutin::ElementState::Pressed,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MouseWheel { delta, .. } => {
|
||||
match delta {
|
||||
glutin::MouseScrollDelta::LineDelta(x, y) => {
|
||||
raw_input.scroll_delta = vec2(x, y) * 24.0;
|
||||
}
|
||||
glutin::MouseScrollDelta::PixelDelta(delta) => {
|
||||
// Actually point delta
|
||||
raw_input.scroll_delta = vec2(delta.x as f32, delta.y as f32);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: HiDpiFactorChanged
|
||||
_ => {
|
||||
// dbg!(event);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn should_ignore_char(chr: char) -> bool {
|
||||
// Glium sends some keys as chars:
|
||||
match chr {
|
||||
'\u{7f}' | // backspace
|
||||
'\u{f728}' | // delete
|
||||
'\u{f700}' | // up
|
||||
'\u{f701}' | // down
|
||||
'\u{f702}' | // left
|
||||
'\u{f703}' | // right
|
||||
'\u{f729}' | // home
|
||||
'\u{f72b}' | // end
|
||||
'\u{f72c}' | // page up
|
||||
'\u{f72d}' | // page down
|
||||
'\u{f710}' | // print screen
|
||||
'\u{f704}' | '\u{f705}' // F1, F2, ...
|
||||
=> true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate_virtual_key_code(key: glutin::VirtualKeyCode) -> Option<egui::Key> {
|
||||
use VirtualKeyCode::*;
|
||||
|
||||
Some(match key {
|
||||
Escape => Key::Escape,
|
||||
Insert => Key::Insert,
|
||||
Home => Key::Home,
|
||||
Delete => Key::Delete,
|
||||
End => Key::End,
|
||||
PageDown => Key::PageDown,
|
||||
PageUp => Key::PageUp,
|
||||
Left => Key::Left,
|
||||
Up => Key::Up,
|
||||
Right => Key::Right,
|
||||
Down => Key::Down,
|
||||
Back => Key::Backspace,
|
||||
Return => Key::Return,
|
||||
// Space => Key::Space,
|
||||
Tab => Key::Tab,
|
||||
|
||||
LAlt | RAlt => Key::Alt,
|
||||
LShift | RShift => Key::Shift,
|
||||
LControl | RControl => Key::Control,
|
||||
LWin | RWin => Key::Logo,
|
||||
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn translate_cursor(cursor_icon: egui::CursorIcon) -> glutin::MouseCursor {
|
||||
match cursor_icon {
|
||||
CursorIcon::Default => glutin::MouseCursor::Default,
|
||||
CursorIcon::PointingHand => glutin::MouseCursor::Hand,
|
||||
CursorIcon::ResizeHorizontal => glutin::MouseCursor::EwResize,
|
||||
CursorIcon::ResizeNeSw => glutin::MouseCursor::NeswResize,
|
||||
CursorIcon::ResizeNwSe => glutin::MouseCursor::NwseResize,
|
||||
CursorIcon::ResizeVertical => glutin::MouseCursor::NsResize,
|
||||
CursorIcon::Text => glutin::MouseCursor::Text,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_output(
|
||||
output: egui::Output,
|
||||
display: &glium::backend::glutin::Display,
|
||||
clipboard: Option<&mut ClipboardContext>,
|
||||
) {
|
||||
if let Some(url) = output.open_url {
|
||||
if let Err(err) = webbrowser::open(&url) {
|
||||
eprintln!("Failed to open url: {}", err); // TODO show error in imgui
|
||||
}
|
||||
}
|
||||
|
||||
if !output.copied_text.is_empty() {
|
||||
if let Some(clipboard) = clipboard {
|
||||
if let Err(err) = clipboard.set_contents(output.copied_text) {
|
||||
eprintln!("Copy/Cut error: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
display
|
||||
.gl_window()
|
||||
.set_cursor(translate_cursor(output.cursor_icon));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
pub fn read_memory(ctx: &Context, memory_json_path: impl AsRef<std::path::Path>) {
|
||||
match std::fs::File::open(memory_json_path) {
|
||||
Ok(file) => {
|
||||
let reader = std::io::BufReader::new(file);
|
||||
match serde_json::from_reader(reader) {
|
||||
Ok(memory) => {
|
||||
*ctx.memory() = memory;
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("ERROR: Failed to parse memory json: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_err) => {
|
||||
// File probably doesn't exist. That's fine.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_memory(
|
||||
ctx: &Context,
|
||||
memory_json_path: impl AsRef<std::path::Path>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
serde_json::to_writer_pretty(std::fs::File::create(memory_json_path)?, &*ctx.memory())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Time of day as seconds since midnight. Used for clock in example app.
|
||||
pub fn local_time_of_day() -> f64 {
|
||||
use chrono::Timelike;
|
||||
let time = chrono::Local::now().time();
|
||||
time.num_seconds_from_midnight() as f64 + 1e-9 * (time.nanosecond() as f64)
|
||||
}
|
||||
306
egui_glium/src/painter.rs
Normal file
306
egui_glium/src/painter.rs
Normal file
@@ -0,0 +1,306 @@
|
||||
#![allow(deprecated)] // legacy implement_vertex macro
|
||||
|
||||
use {
|
||||
egui::{
|
||||
paint::{PaintBatches, Triangles},
|
||||
Rect,
|
||||
},
|
||||
glium::{implement_vertex, index::PrimitiveType, program, texture, uniform, Frame, Surface},
|
||||
};
|
||||
|
||||
pub struct Painter {
|
||||
program: glium::Program,
|
||||
texture: texture::texture2d::Texture2d,
|
||||
current_texture_id: Option<u64>,
|
||||
}
|
||||
|
||||
impl Painter {
|
||||
pub fn new(facade: &dyn glium::backend::Facade) -> Painter {
|
||||
let program = program!(facade,
|
||||
140 => {
|
||||
vertex: "
|
||||
#version 140
|
||||
uniform vec4 u_clip_rect; // min_x, min_y, max_x, max_y
|
||||
uniform vec2 u_screen_size;
|
||||
uniform vec2 u_tex_size;
|
||||
in vec2 a_pos;
|
||||
in vec4 a_color;
|
||||
in vec2 a_tc;
|
||||
out vec2 v_pos;
|
||||
out vec4 v_color;
|
||||
out vec2 v_tc;
|
||||
out vec4 v_clip_rect;
|
||||
void main() {
|
||||
gl_Position = vec4(
|
||||
2.0 * a_pos.x / u_screen_size.x - 1.0,
|
||||
1.0 - 2.0 * a_pos.y / u_screen_size.y,
|
||||
0.0,
|
||||
1.0);
|
||||
v_pos = a_pos;
|
||||
v_color = a_color / 255.0;
|
||||
v_tc = a_tc / u_tex_size;
|
||||
v_clip_rect = u_clip_rect;
|
||||
}
|
||||
",
|
||||
|
||||
fragment: "
|
||||
#version 140
|
||||
uniform sampler2D u_sampler;
|
||||
in vec2 v_pos;
|
||||
in vec4 v_color;
|
||||
in vec2 v_tc;
|
||||
in vec4 v_clip_rect;
|
||||
out vec4 f_color;
|
||||
|
||||
// glium expects linear output.
|
||||
vec3 linear_from_srgb(vec3 srgb) {
|
||||
bvec3 cutoff = lessThan(srgb, vec3(0.04045));
|
||||
vec3 higher = pow((srgb + vec3(0.055)) / vec3(1.055), vec3(2.4));
|
||||
vec3 lower = srgb / vec3(12.92);
|
||||
return mix(higher, lower, cutoff);
|
||||
}
|
||||
|
||||
void main() {
|
||||
if (v_pos.x < v_clip_rect.x) { discard; }
|
||||
if (v_pos.y < v_clip_rect.y) { discard; }
|
||||
if (v_pos.x > v_clip_rect.z) { discard; }
|
||||
if (v_pos.y > v_clip_rect.w) { discard; }
|
||||
f_color = v_color;
|
||||
f_color.rgb = linear_from_srgb(f_color.rgb);
|
||||
f_color *= texture(u_sampler, v_tc).r;
|
||||
}
|
||||
"
|
||||
},
|
||||
|
||||
110 => {
|
||||
vertex: "
|
||||
#version 110
|
||||
uniform vec4 u_clip_rect; // min_x, min_y, max_x, max_y
|
||||
uniform vec2 u_screen_size;
|
||||
uniform vec2 u_tex_size;
|
||||
attribute vec2 a_pos;
|
||||
attribute vec4 a_color;
|
||||
attribute vec2 a_tc;
|
||||
varying vec2 v_pos;
|
||||
varying vec4 v_color;
|
||||
varying vec2 v_tc;
|
||||
varying vec4 v_clip_rect;
|
||||
void main() {
|
||||
gl_Position = vec4(
|
||||
2.0 * a_pos.x / u_screen_size.x - 1.0,
|
||||
1.0 - 2.0 * a_pos.y / u_screen_size.y,
|
||||
0.0,
|
||||
1.0);
|
||||
v_pos = a_pos;
|
||||
v_color = a_color / 255.0;
|
||||
v_tc = a_tc / u_tex_size;
|
||||
v_clip_rect = u_clip_rect;
|
||||
}
|
||||
",
|
||||
|
||||
fragment: "
|
||||
#version 110
|
||||
uniform sampler2D u_sampler;
|
||||
varying vec2 v_pos;
|
||||
varying vec4 v_color;
|
||||
varying vec2 v_tc;
|
||||
varying vec4 v_clip_rect;
|
||||
|
||||
// glium expects linear output.
|
||||
vec3 linear_from_srgb(vec3 srgb) {
|
||||
bvec3 cutoff = lessThan(srgb, vec3(0.04045));
|
||||
vec3 higher = pow((srgb + vec3(0.055)) / vec3(1.055), vec3(2.4));
|
||||
vec3 lower = srgb / vec3(12.92);
|
||||
return mix(higher, lower, cutoff);
|
||||
}
|
||||
|
||||
void main() {
|
||||
if (v_pos.x < v_clip_rect.x) { discard; }
|
||||
if (v_pos.y < v_clip_rect.y) { discard; }
|
||||
if (v_pos.x > v_clip_rect.z) { discard; }
|
||||
if (v_pos.y > v_clip_rect.w) { discard; }
|
||||
gl_FragColor = v_color;
|
||||
gl_FragColor.rgb = linear_from_srgb(gl_FragColor.rgb);
|
||||
gl_FragColor *= texture2D(u_sampler, v_tc).r;
|
||||
}
|
||||
",
|
||||
},
|
||||
|
||||
100 => {
|
||||
vertex: "
|
||||
#version 100
|
||||
uniform mediump vec4 u_clip_rect; // min_x, min_y, max_x, max_y
|
||||
uniform mediump vec2 u_screen_size;
|
||||
uniform mediump vec2 u_tex_size;
|
||||
attribute mediump vec2 a_pos;
|
||||
attribute mediump vec4 a_color;
|
||||
attribute mediump vec2 a_tc;
|
||||
varying mediump vec2 v_pos;
|
||||
varying mediump vec4 v_color;
|
||||
varying mediump vec2 v_tc;
|
||||
varying mediump vec4 v_clip_rect;
|
||||
void main() {
|
||||
gl_Position = vec4(
|
||||
2.0 * a_pos.x / u_screen_size.x - 1.0,
|
||||
1.0 - 2.0 * a_pos.y / u_screen_size.y,
|
||||
0.0,
|
||||
1.0);
|
||||
v_pos = a_pos;
|
||||
v_color = a_color / 255.0;
|
||||
v_tc = a_tc / u_tex_size;
|
||||
v_clip_rect = u_clip_rect;
|
||||
}
|
||||
",
|
||||
|
||||
fragment: "
|
||||
#version 100
|
||||
uniform sampler2D u_sampler;
|
||||
varying mediump vec2 v_pos;
|
||||
varying mediump vec4 v_color;
|
||||
varying mediump vec2 v_tc;
|
||||
varying mediump vec4 v_clip_rect
|
||||
|
||||
// glium expects linear output.
|
||||
vec3 linear_from_srgb(vec3 srgb) {
|
||||
bvec3 cutoff = lessThan(srgb, vec3(0.04045));
|
||||
vec3 higher = pow((srgb + vec3(0.055)) / vec3(1.055), vec3(2.4));
|
||||
vec3 lower = srgb / vec3(12.92);
|
||||
return mix(higher, lower, cutoff);
|
||||
}
|
||||
|
||||
void main() {
|
||||
if (v_pos.x < v_clip_rect.x) { discard; }
|
||||
if (v_pos.y < v_clip_rect.y) { discard; }
|
||||
if (v_pos.x > v_clip_rect.z) { discard; }
|
||||
if (v_pos.y > v_clip_rect.w) { discard; }
|
||||
gl_FragColor = v_color;
|
||||
gl_FragColor.rgb = linear_from_srgb(gl_FragColor.rgb);
|
||||
gl_FragColor *= texture2D(u_sampler, v_tc).r;
|
||||
}
|
||||
",
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let pixels = vec![vec![255u8, 0u8], vec![0u8, 255u8]];
|
||||
let format = texture::UncompressedFloatFormat::U8;
|
||||
let mipmaps = texture::MipmapsOption::NoMipmap;
|
||||
let texture =
|
||||
texture::texture2d::Texture2d::with_format(facade, pixels, format, mipmaps).unwrap();
|
||||
|
||||
Painter {
|
||||
program,
|
||||
texture,
|
||||
current_texture_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn upload_texture(&mut self, facade: &dyn glium::backend::Facade, texture: &egui::Texture) {
|
||||
if self.current_texture_id == Some(texture.id) {
|
||||
return; // No change
|
||||
}
|
||||
|
||||
let pixels: Vec<Vec<u8>> = texture
|
||||
.pixels
|
||||
.chunks(texture.width as usize)
|
||||
.map(|row| row.to_vec())
|
||||
.collect();
|
||||
|
||||
let format = texture::UncompressedFloatFormat::U8;
|
||||
let mipmaps = texture::MipmapsOption::NoMipmap;
|
||||
self.texture =
|
||||
texture::texture2d::Texture2d::with_format(facade, pixels, format, mipmaps).unwrap();
|
||||
self.current_texture_id = Some(texture.id);
|
||||
}
|
||||
|
||||
pub fn paint_batches(
|
||||
&mut self,
|
||||
display: &glium::Display,
|
||||
batches: PaintBatches,
|
||||
texture: &egui::Texture,
|
||||
) {
|
||||
self.upload_texture(display, texture);
|
||||
|
||||
let mut target = display.draw();
|
||||
target.clear_color(0.0, 0.0, 0.0, 0.0);
|
||||
for (clip_rect, triangles) in batches {
|
||||
self.paint_batch(&mut target, display, clip_rect, &triangles, texture)
|
||||
}
|
||||
target.finish().unwrap();
|
||||
}
|
||||
|
||||
#[inline(never)] // Easier profiling
|
||||
fn paint_batch(
|
||||
&mut self,
|
||||
target: &mut Frame,
|
||||
display: &glium::Display,
|
||||
clip_rect: Rect,
|
||||
triangles: &Triangles,
|
||||
texture: &egui::Texture,
|
||||
) {
|
||||
let vertex_buffer = {
|
||||
#[derive(Copy, Clone)]
|
||||
struct Vertex {
|
||||
a_pos: [f32; 2],
|
||||
a_color: [u8; 4],
|
||||
a_tc: [u16; 2],
|
||||
}
|
||||
implement_vertex!(Vertex, a_pos, a_color, a_tc);
|
||||
|
||||
let vertices: Vec<Vertex> = triangles
|
||||
.vertices
|
||||
.iter()
|
||||
.map(|v| Vertex {
|
||||
a_pos: [v.pos.x, v.pos.y],
|
||||
a_color: [v.color.r, v.color.g, v.color.b, v.color.a],
|
||||
a_tc: [v.uv.0, v.uv.1],
|
||||
})
|
||||
.collect();
|
||||
|
||||
glium::VertexBuffer::new(display, &vertices).unwrap()
|
||||
};
|
||||
|
||||
let indices: Vec<u32> = triangles.indices.iter().map(|idx| *idx as u32).collect();
|
||||
|
||||
let index_buffer =
|
||||
glium::IndexBuffer::new(display, PrimitiveType::TrianglesList, &indices).unwrap();
|
||||
|
||||
let pixels_per_point = display.gl_window().get_hidpi_factor() as f32;
|
||||
let (width_pixels, height_pixels) = display.get_framebuffer_dimensions();
|
||||
let width_points = width_pixels as f32 / pixels_per_point;
|
||||
let height_points = height_pixels as f32 / pixels_per_point;
|
||||
|
||||
let uniforms = uniform! {
|
||||
u_clip_rect: [clip_rect.min.x, clip_rect.min.y, clip_rect.max.x, clip_rect.max.y],
|
||||
u_screen_size: [width_points, height_points],
|
||||
u_tex_size: [texture.width as f32, texture.height as f32],
|
||||
u_sampler: &self.texture,
|
||||
};
|
||||
|
||||
// Emilib outputs colors with premultiplied alpha:
|
||||
let blend_func = glium::BlendingFunction::Addition {
|
||||
source: glium::LinearBlendingFactor::One,
|
||||
destination: glium::LinearBlendingFactor::OneMinusSourceAlpha,
|
||||
};
|
||||
let blend = glium::Blend {
|
||||
color: blend_func,
|
||||
alpha: blend_func,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let params = glium::DrawParameters {
|
||||
blend,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
target
|
||||
.draw(
|
||||
&vertex_buffer,
|
||||
&index_buffer,
|
||||
&self.program,
|
||||
&uniforms,
|
||||
¶ms,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user