mirror of
https://github.com/emilk/egui.git
synced 2026-06-28 07:23:13 -04:00
Raw mouse movement is unaccelerated and unclamped by screen boundaries, and does not relate to any position on the screen. It is useful in certain situations such as draggable values and 3D cameras, where screen position does not matter. https://github.com/emilk/egui/assets/1700581/1400e6a6-0573-41b9-99a1-a9cd305aa1a3 Added `Event::MouseMoved` for integrations to supply raw mouse movement. Added `Response:drag_motion` to get the raw mouse movement, but will fall back to delta in case the integration does not supply it. Nothing should be breaking, but third-party integrations that can send `Event::MouseMoved` should be updated to do so. Based on #1614 but updated to the current version, and with better fallback behaviour. * Closes #1611 * Supersedes #1614
203 lines
6.3 KiB
Rust
203 lines
6.3 KiB
Rust
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
|
#![allow(unsafe_code)]
|
|
|
|
use eframe::{egui, egui_glow, glow};
|
|
|
|
use egui::mutex::Mutex;
|
|
use std::sync::Arc;
|
|
|
|
fn main() -> Result<(), eframe::Error> {
|
|
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
|
let options = eframe::NativeOptions {
|
|
viewport: egui::ViewportBuilder::default().with_inner_size([350.0, 380.0]),
|
|
multisampling: 4,
|
|
renderer: eframe::Renderer::Glow,
|
|
..Default::default()
|
|
};
|
|
eframe::run_native(
|
|
"Custom 3D painting in eframe using glow",
|
|
options,
|
|
Box::new(|cc| Box::new(MyApp::new(cc))),
|
|
)
|
|
}
|
|
|
|
struct MyApp {
|
|
/// Behind an `Arc<Mutex<…>>` so we can pass it to [`egui::PaintCallback`] and paint later.
|
|
rotating_triangle: Arc<Mutex<RotatingTriangle>>,
|
|
angle: f32,
|
|
}
|
|
|
|
impl MyApp {
|
|
fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
|
let gl = cc
|
|
.gl
|
|
.as_ref()
|
|
.expect("You need to run eframe with the glow backend");
|
|
Self {
|
|
rotating_triangle: Arc::new(Mutex::new(RotatingTriangle::new(gl))),
|
|
angle: 0.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl eframe::App for MyApp {
|
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
|
egui::CentralPanel::default().show(ctx, |ui| {
|
|
ui.horizontal(|ui| {
|
|
ui.spacing_mut().item_spacing.x = 0.0;
|
|
ui.label("The triangle is being painted using ");
|
|
ui.hyperlink_to("glow", "https://github.com/grovesNL/glow");
|
|
ui.label(" (OpenGL).");
|
|
});
|
|
|
|
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
|
self.custom_painting(ui);
|
|
});
|
|
ui.label("Drag to rotate!");
|
|
});
|
|
}
|
|
|
|
fn on_exit(&mut self, gl: Option<&glow::Context>) {
|
|
if let Some(gl) = gl {
|
|
self.rotating_triangle.lock().destroy(gl);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl MyApp {
|
|
fn custom_painting(&mut self, ui: &mut egui::Ui) {
|
|
let (rect, response) =
|
|
ui.allocate_exact_size(egui::Vec2::splat(300.0), egui::Sense::drag());
|
|
|
|
self.angle += response.drag_motion().x * 0.01;
|
|
|
|
// Clone locals so we can move them into the paint callback:
|
|
let angle = self.angle;
|
|
let rotating_triangle = self.rotating_triangle.clone();
|
|
|
|
let callback = egui::PaintCallback {
|
|
rect,
|
|
callback: std::sync::Arc::new(egui_glow::CallbackFn::new(move |_info, painter| {
|
|
rotating_triangle.lock().paint(painter.gl(), angle);
|
|
})),
|
|
};
|
|
ui.painter().add(callback);
|
|
}
|
|
}
|
|
|
|
struct RotatingTriangle {
|
|
program: glow::Program,
|
|
vertex_array: glow::VertexArray,
|
|
}
|
|
|
|
impl RotatingTriangle {
|
|
fn new(gl: &glow::Context) -> Self {
|
|
use glow::HasContext as _;
|
|
|
|
let shader_version = if cfg!(target_arch = "wasm32") {
|
|
"#version 300 es"
|
|
} else {
|
|
"#version 330"
|
|
};
|
|
|
|
unsafe {
|
|
let program = gl.create_program().expect("Cannot create program");
|
|
|
|
let (vertex_shader_source, fragment_shader_source) = (
|
|
r#"
|
|
const vec2 verts[3] = vec2[3](
|
|
vec2(0.0, 1.0),
|
|
vec2(-1.0, -1.0),
|
|
vec2(1.0, -1.0)
|
|
);
|
|
const vec4 colors[3] = vec4[3](
|
|
vec4(1.0, 0.0, 0.0, 1.0),
|
|
vec4(0.0, 1.0, 0.0, 1.0),
|
|
vec4(0.0, 0.0, 1.0, 1.0)
|
|
);
|
|
out vec4 v_color;
|
|
uniform float u_angle;
|
|
void main() {
|
|
v_color = colors[gl_VertexID];
|
|
gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0);
|
|
gl_Position.x *= cos(u_angle);
|
|
}
|
|
"#,
|
|
r#"
|
|
precision mediump float;
|
|
in vec4 v_color;
|
|
out vec4 out_color;
|
|
void main() {
|
|
out_color = v_color;
|
|
}
|
|
"#,
|
|
);
|
|
|
|
let shader_sources = [
|
|
(glow::VERTEX_SHADER, vertex_shader_source),
|
|
(glow::FRAGMENT_SHADER, fragment_shader_source),
|
|
];
|
|
|
|
let shaders: Vec<_> = shader_sources
|
|
.iter()
|
|
.map(|(shader_type, shader_source)| {
|
|
let shader = gl
|
|
.create_shader(*shader_type)
|
|
.expect("Cannot create shader");
|
|
gl.shader_source(shader, &format!("{shader_version}\n{shader_source}"));
|
|
gl.compile_shader(shader);
|
|
assert!(
|
|
gl.get_shader_compile_status(shader),
|
|
"Failed to compile {shader_type}: {}",
|
|
gl.get_shader_info_log(shader)
|
|
);
|
|
gl.attach_shader(program, shader);
|
|
shader
|
|
})
|
|
.collect();
|
|
|
|
gl.link_program(program);
|
|
assert!(
|
|
gl.get_program_link_status(program),
|
|
"{}",
|
|
gl.get_program_info_log(program)
|
|
);
|
|
|
|
for shader in shaders {
|
|
gl.detach_shader(program, shader);
|
|
gl.delete_shader(shader);
|
|
}
|
|
|
|
let vertex_array = gl
|
|
.create_vertex_array()
|
|
.expect("Cannot create vertex array");
|
|
|
|
Self {
|
|
program,
|
|
vertex_array,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn destroy(&self, gl: &glow::Context) {
|
|
use glow::HasContext as _;
|
|
unsafe {
|
|
gl.delete_program(self.program);
|
|
gl.delete_vertex_array(self.vertex_array);
|
|
}
|
|
}
|
|
|
|
fn paint(&self, gl: &glow::Context, angle: f32) {
|
|
use glow::HasContext as _;
|
|
unsafe {
|
|
gl.use_program(Some(self.program));
|
|
gl.uniform_1_f32(
|
|
gl.get_uniform_location(self.program, "u_angle").as_ref(),
|
|
angle,
|
|
);
|
|
gl.bind_vertex_array(Some(self.vertex_array));
|
|
gl.draw_arrays(glow::TRIANGLES, 0, 3);
|
|
}
|
|
}
|
|
}
|