Created
November 30, 2020 21:32
-
-
Save smokku/c5598d32092141c65cbf53821cb1aeb0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use crate::{constants, GameGraphics}; | |
use bevy::{ | |
app::startup_stage, | |
input::{ | |
keyboard::{KeyCode, KeyboardInput}, | |
mouse::MouseButtonInput, | |
}, | |
math::{vec2, Vec2}, | |
prelude::*, | |
}; | |
use gfx2d::{FilterMethod, Gfx2dContext, Texture, Vertex, WrapMode}; | |
use megaui::{self, InputHandler}; | |
// use std::collections::HashMap; | |
pub use megaui::{hash, widgets, Drag, Vector2}; | |
#[derive(Default)] | |
pub struct MegaUiPlugin; | |
impl Plugin for MegaUiPlugin { | |
fn build(&self, app: &mut AppBuilder) { | |
app.add_startup_system_to_stage(startup_stage::PRE_STARTUP, init_megaui) | |
.add_resource(EventsState::default()) | |
.add_system_to_stage_front(stage::PRE_UPDATE, process_input) | |
.add_system_to_stage(stage::POST_UPDATE, draw_megaui); | |
} | |
} | |
pub struct UiContext { | |
ui: megaui::Ui, | |
ui_draw_list: Vec<megaui::DrawList>, | |
font_texture: Texture, | |
// megaui_textures: HashMap<u32, Texture>, | |
pub mouse_position: (f32, f32), | |
} | |
// I pinky-promise I will not touch texture atlas outside thread local systems | |
unsafe impl Send for UiContext {} | |
unsafe impl Sync for UiContext {} | |
impl UiContext { | |
fn new(ctx: &mut Gfx2dContext) -> Self { | |
let ui = megaui::Ui::new(); | |
// ui.set_clipboard_object(ClipboardObject); | |
let texture_data = &ui.font_atlas.texture; | |
let font_texture = Texture::new( | |
ctx, | |
(texture_data.width as u16, texture_data.height as u16), | |
&texture_data.data, | |
FilterMethod::Scale, | |
WrapMode::Clamp, | |
); | |
UiContext { | |
ui, | |
ui_draw_list: Vec::new(), | |
font_texture, | |
// megaui_textures: HashMap::new(), | |
mouse_position: (0.0, 0.0), | |
} | |
} | |
fn render_draw_list(&mut self) { | |
self.ui_draw_list.clear(); | |
self.ui.render(&mut self.ui_draw_list); | |
} | |
} | |
fn init_megaui(_world: &mut World, resources: &mut Resources) { | |
let ui_context = { | |
let mut ctx = resources.get_thread_local_mut::<Gfx2dContext>().unwrap(); | |
UiContext::new(&mut *ctx) | |
}; | |
resources.insert(ui_context); | |
} | |
fn draw_megaui(_world: &mut World, resources: &mut Resources) { | |
let mut ctx = resources.get_mut::<UiContext>().unwrap(); | |
ctx.render_draw_list(); | |
let mut graphics = resources.get_mut::<GameGraphics>().unwrap(); | |
for draw_command in &ctx.ui_draw_list { | |
// println!("{:?}", draw_command); | |
if draw_command.texture.is_some() { | |
panic!("got draw command texture {:?}", draw_command.texture); | |
} | |
// FIXME: draw_command.clipping_zone | |
graphics.add_ui_geometry( | |
Some(&ctx.font_texture), | |
&draw_command | |
.vertices | |
.iter() | |
.map(|vert| { | |
use std::convert::TryInto; | |
Vertex { | |
pos: [vert.pos[0], vert.pos[1]], | |
texcoords: vert.uv, | |
color: vert | |
.color | |
.iter() | |
.map(|v| ::gfx::format::U8Norm((v * 255.) as u8)) | |
.collect::<Vec<::gfx::format::U8Norm>>() | |
.try_into() | |
.unwrap(), | |
} | |
}) | |
.collect::<Vec<Vertex>>(), | |
&draw_command.indices, | |
); | |
} | |
let time = resources.get::<Time>().unwrap(); | |
ctx.ui.new_frame(time.delta_seconds); | |
} | |
pub struct WindowParams { | |
pub label: String, | |
pub movable: bool, | |
pub close_button: bool, | |
pub titlebar: bool, | |
} | |
impl Default for WindowParams { | |
fn default() -> WindowParams { | |
WindowParams { | |
label: "".to_string(), | |
movable: true, | |
close_button: false, | |
titlebar: true, | |
} | |
} | |
} | |
#[allow(dead_code)] | |
impl UiContext { | |
pub fn set_ui_style(&mut self, style: megaui::Style) { | |
self.ui.set_style(style); | |
} | |
// pub fn set_megaui_texture(&mut self, id: u32, texture: Texture) { | |
// /// TODO: check whether in thread_local_system | |
// self.megaui_textures.insert(id, texture); | |
// } | |
pub fn draw_window<F: FnOnce(&mut megaui::Ui)>( | |
&mut self, | |
id: megaui::Id, | |
position: Vec2, | |
size: Vec2, | |
params: impl Into<Option<WindowParams>>, | |
f: F, | |
) -> bool { | |
let ui = &mut self.ui; | |
let params = params.into(); | |
megaui::widgets::Window::new( | |
id, | |
megaui::Vector2::new(position.x, position.y), | |
megaui::Vector2::new(size.x, size.y), | |
) | |
.label(params.as_ref().map_or("", |params| ¶ms.label)) | |
.titlebar(params.as_ref().map_or(true, |params| params.titlebar)) | |
.movable(params.as_ref().map_or(true, |params| params.movable)) | |
.close_button(params.as_ref().map_or(false, |params| params.close_button)) | |
.ui(ui, f) | |
} | |
/// Check for megaui mouse overlap | |
pub fn mouse_over_ui(&self) -> bool { | |
self.ui.is_mouse_over(megaui::Vector2::new( | |
self.mouse_position.0, | |
self.mouse_position.1, | |
)) | |
} | |
} | |
#[derive(Default)] | |
struct EventsState { | |
cursor_moved_event_reader: EventReader<CursorMoved>, | |
char_input_events_reader: EventReader<ReceivedCharacter>, | |
} | |
#[allow(clippy::too_many_arguments)] | |
fn process_input( | |
mut ctx: ResMut<UiContext>, | |
mut events: Local<EventsState>, | |
cursor_moved_events: Res<Events<CursorMoved>>, | |
mut mouse_button_input: ResMut<Input<MouseButton>>, | |
mut char_input_events: ResMut<Events<ReceivedCharacter>>, | |
keyboard_input: Res<Input<KeyCode>>, | |
mut mouse_button_input_events: ResMut<Events<MouseButtonInput>>, | |
mut keyboard_input_events: ResMut<Events<KeyboardInput>>, | |
) { | |
for event in events.cursor_moved_event_reader.iter(&cursor_moved_events) { | |
if event.id.is_primary() { | |
ctx.mouse_position = ( | |
event.position.x, | |
(constants::WINDOW_HEIGHT as f32 - event.position.y), | |
); | |
} | |
} | |
let mouse_position = ctx.mouse_position; | |
ctx.ui.mouse_move(mouse_position); | |
if mouse_button_input.just_pressed(MouseButton::Left) { | |
ctx.ui.mouse_down(mouse_position); | |
} | |
if mouse_button_input.just_released(MouseButton::Left) { | |
ctx.ui.mouse_up(mouse_position); | |
} | |
let shift = keyboard_input.pressed(KeyCode::LShift) || keyboard_input.pressed(KeyCode::RShift); | |
let ctrl = | |
keyboard_input.pressed(KeyCode::LControl) || keyboard_input.pressed(KeyCode::RControl); | |
for event in events.char_input_events_reader.iter(&char_input_events) { | |
if event.id.is_primary() && !event.char.is_control() { | |
ctx.ui.char_event(event.char, shift, ctrl); | |
} | |
} | |
if keyboard_input.pressed(KeyCode::Up) { | |
ctx.ui.key_down(megaui::KeyCode::Up, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::Down) { | |
ctx.ui.key_down(megaui::KeyCode::Down, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::Right) { | |
ctx.ui.key_down(megaui::KeyCode::Right, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::Left) { | |
ctx.ui.key_down(megaui::KeyCode::Left, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::Home) { | |
ctx.ui.key_down(megaui::KeyCode::Home, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::End) { | |
ctx.ui.key_down(megaui::KeyCode::End, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::Delete) { | |
ctx.ui.key_down(megaui::KeyCode::Delete, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::Back) { | |
ctx.ui.key_down(megaui::KeyCode::Backspace, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::Return) { | |
ctx.ui.key_down(megaui::KeyCode::Enter, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::Tab) { | |
ctx.ui.key_down(megaui::KeyCode::Tab, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::Z) { | |
ctx.ui.key_down(megaui::KeyCode::Z, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::Y) { | |
ctx.ui.key_down(megaui::KeyCode::Y, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::C) { | |
ctx.ui.key_down(megaui::KeyCode::C, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::X) { | |
ctx.ui.key_down(megaui::KeyCode::X, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::V) { | |
ctx.ui.key_down(megaui::KeyCode::V, shift, ctrl); | |
} | |
if keyboard_input.pressed(KeyCode::A) { | |
ctx.ui.key_down(megaui::KeyCode::A, shift, ctrl); | |
} | |
if ctx.mouse_over_ui() { | |
mouse_button_input.update(); | |
char_input_events.update(); | |
// keyboard_input.update(); | |
mouse_button_input_events.update(); | |
keyboard_input_events.update(); | |
} | |
} | |
#[allow(dead_code)] | |
#[derive(Default)] | |
pub struct DemoState { | |
data0: String, | |
data1: String, | |
text0: String, | |
text1: String, | |
number0: f32, | |
number1: f32, | |
} | |
#[allow(dead_code)] | |
pub fn draw_demo(mut ctx: ResMut<UiContext>, mut state: Local<DemoState>) { | |
ctx.draw_window( | |
hash!(), | |
vec2(470., 50.), | |
vec2(300., 300.), | |
WindowParams { | |
label: "Megaui Showcase Window".to_string(), | |
..Default::default() | |
}, | |
|ui| { | |
ui.tree_node(hash!(), "input", |ui| { | |
ui.label(None, "Some random text"); | |
if ui.button(None, "click me") { | |
println!("hi"); | |
} | |
ui.separator(); | |
ui.label(None, "Some other random text"); | |
if ui.button(None, "other button") { | |
println!("hi2"); | |
} | |
ui.separator(); | |
ui.input_field(hash!(), "<- input text 1", &mut state.data0); | |
ui.input_field(hash!(), "<- input text 2", &mut state.data1); | |
ui.label( | |
None, | |
&format!("Text entered: \"{}\" and \"{}\"", state.data0, state.data1), | |
); | |
ui.separator(); | |
}); | |
ui.tree_node(hash!(), "sliders", |ui| { | |
ui.slider(hash!(), "[-10 .. 10]", -10f32..10f32, &mut state.number0); | |
ui.slider(hash!(), "[0 .. 100]", 0f32..100f32, &mut state.number1); | |
}); | |
ui.tree_node(hash!(), "editbox 1", |ui| { | |
ui.label(None, "This is editbox!"); | |
ui.editbox(hash!(), megaui::Vector2::new(285., 165.), &mut state.text0); | |
}); | |
ui.tree_node(hash!(), "editbox 2", |ui| { | |
ui.label(None, "This is editbox!"); | |
ui.editbox(hash!(), megaui::Vector2::new(285., 165.), &mut state.text1); | |
}); | |
}, | |
); | |
ctx.draw_window( | |
hash!(), | |
vec2(10., 10.), | |
vec2(50., 20.), | |
WindowParams { | |
titlebar: false, | |
..Default::default() | |
}, | |
|ui| { | |
ui.label(None, "Hello!"); | |
}, | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment