Skip to content

Instantly share code, notes, and snippets.

@smokku
Created November 30, 2020 21:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save smokku/c5598d32092141c65cbf53821cb1aeb0 to your computer and use it in GitHub Desktop.
Save smokku/c5598d32092141c65cbf53821cb1aeb0 to your computer and use it in GitHub Desktop.
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| &params.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