2022-03-04 13:29:01 +01:00

235 lines
8.0 KiB
Rust

use glium::glutin;
use glium::glutin::event::{Event, WindowEvent};
use glium::glutin::event_loop::{ControlFlow, EventLoop};
use glium::glutin::window::WindowBuilder;
use glium::{Display, Surface};
use imgui::{Context, FontConfig, FontGlyphRanges, FontSource, Ui, ViewportFlags};
use imgui_glium_renderer::Renderer;
use imgui_winit_support::{HiDpiMode, WinitPlatform};
use std::collections::HashMap;
use std::path::Path;
use std::time::Instant;
mod clipboard;
pub struct System {
pub event_loop: EventLoop<()>,
pub display: glium::Display,
pub imgui: Context,
pub platform: WinitPlatform,
pub renderer: Renderer,
pub font_size: f32,
}
pub fn init(title: &str) -> System {
let title = match Path::new(&title).file_name() {
Some(file_name) => file_name.to_str().unwrap(),
None => title,
};
let event_loop = EventLoop::new();
let context = glutin::ContextBuilder::new().with_vsync(true);
let builder = WindowBuilder::new()
.with_title(title.to_owned())
.with_inner_size(glutin::dpi::LogicalSize::new(1024f64, 768f64));
let display =
Display::new(builder, context, &event_loop).expect("Failed to initialize display");
let mut imgui = Context::create();
imgui.set_ini_filename(None);
if let Some(backend) = clipboard::init() {
imgui.set_clipboard_backend(backend);
} else {
eprintln!("Failed to initialize clipboard");
}
let mut platform = WinitPlatform::init(&mut imgui);
WinitPlatform::init_viewports(&mut imgui, display.gl_window().window(), &event_loop);
{
let gl_window = display.gl_window();
let window = gl_window.window();
let dpi_mode = if let Ok(factor) = std::env::var("IMGUI_EXAMPLE_FORCE_DPI_FACTOR") {
// Allow forcing of HiDPI factor for debugging purposes
match factor.parse::<f64>() {
Ok(f) => HiDpiMode::Locked(f),
Err(e) => panic!("Invalid scaling factor: {}", e),
}
} else {
HiDpiMode::Default
};
platform.attach_window(imgui.io_mut(), window, dpi_mode);
}
// Fixed font size. Note imgui_winit_support uses "logical
// pixels", which are physical pixels scaled by the devices
// scaling factor. Meaning, 13.0 pixels should look the same size
// on two different screens, and thus we do not need to scale this
// value (as the scaling is handled by winit)
let font_size = 13.0;
imgui.fonts().add_font(&[
FontSource::TtfData {
data: include_bytes!("../../../resources/Roboto-Regular.ttf"),
size_pixels: font_size,
config: Some(FontConfig {
// As imgui-glium-renderer isn't gamma-correct with
// it's font rendering, we apply an arbitrary
// multiplier to make the font a bit "heavier". With
// default imgui-glow-renderer this is unnecessary.
rasterizer_multiply: 1.5,
// Oversampling font helps improve text rendering at
// expense of larger font atlas texture.
oversample_h: 4,
oversample_v: 4,
..FontConfig::default()
}),
},
FontSource::TtfData {
data: include_bytes!("../../../resources/mplus-1p-regular.ttf"),
size_pixels: font_size,
config: Some(FontConfig {
// Oversampling font helps improve text rendering at
// expense of larger font atlas texture.
oversample_h: 4,
oversample_v: 4,
// Range of glyphs to rasterize
glyph_ranges: FontGlyphRanges::japanese(),
..FontConfig::default()
}),
},
]);
let renderer = Renderer::init(&mut imgui, &display).expect("Failed to initialize renderer");
System {
event_loop,
display,
imgui,
platform,
renderer,
font_size,
}
}
struct ViewportStorage<'a, T: 'static> {
event_loop: &'a glium::glutin::event_loop::EventLoopWindowTarget<T>,
viewports: &'a mut HashMap<imgui::Id, glium::glutin::window::Window>,
}
impl<'a, T> imgui_winit_support::WinitPlatformViewportStorage for ViewportStorage<'a, T> {
fn create_window(&mut self, id: imgui::Id, flags: imgui::ViewportFlags) {
let builder = WindowBuilder::new()
.with_always_on_top(flags.contains(ViewportFlags::TOP_MOST))
// .with_decorations(!flags.contains(ViewportFlags::NO_DECORATION))
.with_resizable(true)
.with_visible(false);
let window = builder.build(self.event_loop).unwrap();
self.viewports.insert(id, window);
}
fn remove_windows(&mut self, filter: impl Fn(imgui::Id) -> bool) {
self.viewports.retain(|id, _| !filter(*id));
}
fn get_window(
&mut self,
id: glium::glutin::window::WindowId,
) -> Option<(imgui::Id, &glium::glutin::window::Window)> {
let res = self
.viewports
.iter()
.find(|(_, wnd)| wnd.id() == id)
.map(|(id, wnd)| (*id, wnd));
res
}
fn for_each(&mut self, mut func: impl FnMut(imgui::Id, &glium::glutin::window::Window)) {
for (id, wnd) in self.viewports.iter() {
func(*id, wnd);
}
}
}
impl System {
pub fn main_loop<F: FnMut(&mut bool, &mut Ui) + 'static>(self, mut run_ui: F) {
let System {
event_loop,
display,
mut imgui,
mut platform,
mut renderer,
..
} = self;
let mut last_frame = Instant::now();
let mut viewports = HashMap::new();
event_loop.run(move |event, window_target, control_flow| match event {
Event::NewEvents(_) => {
let now = Instant::now();
imgui.io_mut().update_delta_time(now - last_frame);
last_frame = now;
}
Event::MainEventsCleared => {
let gl_window = display.gl_window();
platform
.prepare_frame(imgui.io_mut(), gl_window.window())
.expect("Failed to prepare frame");
gl_window.window().request_redraw();
let mut storage = ViewportStorage {
event_loop: window_target,
viewports: &mut viewports,
};
platform.update_viewports(&mut imgui, &mut storage);
}
Event::RedrawRequested(_) => {
let ui = imgui.frame();
let mut run = true;
run_ui(&mut run, ui);
if !run {
*control_flow = ControlFlow::Exit;
}
let gl_window = display.gl_window();
let mut target = display.draw();
target.clear_color_srgb(1.0, 1.0, 1.0, 1.0);
platform.prepare_render(ui, gl_window.window());
let draw_data = imgui.render();
renderer
.render(&mut target, draw_data)
.expect("Rendering failed");
target.finish().expect("Failed to swap buffers");
imgui.update_platform_windows();
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
..
} if window_id == display.gl_window().window().id() => {
*control_flow = ControlFlow::Exit
}
event => {
let gl_window = display.gl_window();
platform.handle_event(imgui.io_mut(), gl_window.window(), &event);
let mut storage = ViewportStorage {
event_loop: window_target,
viewports: &mut viewports,
};
platform.handle_viewport_event(
&mut imgui,
gl_window.window(),
&mut storage,
&event,
);
}
})
}
}