From ce4ac023c27eeb4cb6aa82cb749ecce1c1fa0c5b Mon Sep 17 00:00:00 2001 From: f0e <7321764+f0e@users.noreply.github.com> Date: Sun, 24 Nov 2024 21:56:08 +1000 Subject: [PATCH] feat: open file button, keypress/mouse wrapper, render on handled input, rounded rect stroke --- src/gui/gui.cpp | 86 +++++++++++++++++++++++++++++-------------- src/gui/gui.h | 4 +- src/gui/ui/keys.cpp | 19 ++++++++++ src/gui/ui/keys.h | 12 ++++++ src/gui/ui/render.cpp | 33 ++++++++++++++--- src/gui/ui/render.h | 1 + src/gui/ui/ui.cpp | 56 +++++++++++++++++++++++++++- src/gui/ui/ui.h | 19 +++++++++- 8 files changed, 192 insertions(+), 38 deletions(-) create mode 100644 src/gui/ui/keys.cpp create mode 100644 src/gui/ui/keys.h diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 768b1ea..e7ae54c 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -7,11 +7,12 @@ #include "tasks.h" #include "ui/ui.h" +#include "ui/keys.h" #include "ui/utils.h" #include "resources/fonts.h" -#define DEBUG_RENDER 0 +#define DEBUG_RENDER 1 const int font_height = 14; const int pad_x = 24; @@ -25,7 +26,6 @@ const float default_delta_time = 1.f / 60; float vsync_frame_time = default_delta_time; bool closing = false; -bool actively_rendering = true; SkFont font; SkFont header_font; @@ -80,12 +80,6 @@ static os::WindowRef create_window(os::DragTarget& dragTarget) { bool processEvent(const os::Event& ev) { switch (ev.type()) { - case os::Event::KeyDown: { - if (ev.scancode() == os::kKeyEsc) - return false; - break; - } - case os::Event::CloseApp: case os::Event::CloseWindow: { closing = true; @@ -95,35 +89,62 @@ bool processEvent(const os::Event& ev) { case os::Event::ResizeWindow: break; + case os::Event::MouseMove: { + printf("mouse move bro! %d %d\n", ev.position().x, ev.position().y); + keys::on_mouse_move( + ev.position(), + ev.modifiers(), + ev.pointerType(), + ev.pressure() + ); + return true; + } + case os::Event::MouseDown: { - base::paths paths; - utils::show_file_selector("Blur input", ".", { "mp4", "mkv" }, os::FileDialog::Type::OpenFiles, paths); - tasks::add_files(paths); - break; + keys::on_mouse_down( + ev.position(), + ev.button(), + ev.modifiers(), + ev.pointerType(), + ev.pressure() + ); + return true; + } + + case os::Event::MouseUp: { + keys::on_mouse_up( + ev.position(), + ev.button(), + ev.modifiers(), + ev.pointerType() + ); + return true; } default: break; } - return true; + return false; } -void gui::generate_messages_from_os_events() { // https://github.com/aseprite/aseprite/blob/45c2a5950445c884f5d732edc02989c3fb6ab1a6/src/ui/manager.cpp#L393 +bool gui::generate_messages_from_os_events(bool rendered_last) { // https://github.com/aseprite/aseprite/blob/45c2a5950445c884f5d732edc02989c3fb6ab1a6/src/ui/manager.cpp#L393 os::Event event; double timeout; - if (actively_rendering) + if (rendered_last) timeout = 0.0; else timeout = 1.f / 60; event_queue->getEvent(event, timeout); - processEvent(event); + return processEvent(event); } void gui::event_loop() { + bool to_render = true; + while (!closing) { #ifdef _WIN32 HMONITOR screen_handle = (HMONITOR)window->screen()->nativeHandle(); @@ -145,10 +166,14 @@ void gui::event_loop() { auto frame_start = std::chrono::steady_clock::now(); - redraw_window(window.get(), false); - generate_messages_from_os_events(); + bool rendered = redraw_window(window.get(), to_render); + to_render = generate_messages_from_os_events(rendered); // true if input handled + +#if DEBUG_RENDER + printf("rendered: %d, to render: %d\n", rendered, to_render); +#endif - if (actively_rendering) { + if (rendered || to_render) { auto target_time = frame_start + std::chrono::duration_cast( std::chrono::duration(vsync_frame_time) ); @@ -157,7 +182,7 @@ void gui::event_loop() { } } -void gui::redraw_window(os::Window* window, bool force_render) { +bool gui::redraw_window(os::Window* window, bool force_render) { auto now = std::chrono::steady_clock::now(); static auto last_frame_time = now; @@ -193,14 +218,12 @@ void gui::redraw_window(os::Window* window, bool force_render) { os::Surface* s = window->surface(); const gfx::Rect rc = s->bounds(); - bool updated = false; - gfx::Rect drop_zone = rc; static float bg_overlay_shade = 0.f; float last_fill_shade = bg_overlay_shade; bg_overlay_shade = std::lerp(bg_overlay_shade, windowData.dragging ? 30.f : 0.f, 25.f * delta_time); - updated |= bg_overlay_shade != last_fill_shade; + force_render |= bg_overlay_shade != last_fill_shade; { // const int blur_stroke_width = 1; @@ -243,7 +266,12 @@ void gui::redraw_window(os::Window* window, bool force_render) { title_pos.y = pad_y + header_font.getSize(); ui::add_text_fixed("blur title text", container, title_pos, "blur", gfx::rgba(255, 255, 255, 255), header_font, os::TextAlign::Center, 15); - ui::add_text("drop a file text", container, "Drop a file...", gfx::rgba(255, 255, 255, 255), font, os::TextAlign::Center); + ui::add_button("open a file button", container, "Open a file", font, [] { + base::paths paths; + utils::show_file_selector("Blur input", ".", { "mp4", "mkv" }, os::FileDialog::Type::OpenFiles, paths); + tasks::add_files(paths); + }); + ui::add_text("drop a file text", container, "or drop a file anywhere", gfx::rgba(255, 255, 255, 255), font, os::TextAlign::Center); } else { bool is_progress_shown = false; @@ -291,11 +319,10 @@ void gui::redraw_window(os::Window* window, bool force_render) { ui::center_elements_in_container(container); - bool last = actively_rendering; - actively_rendering = ui::update_container(s, container, delta_time) || updated || force_render; - if (!actively_rendering) + bool want_to_render = ui::update_container(s, container, delta_time) || force_render; + if (!want_to_render) // note: DONT RENDER ANYTHING ABOVE HERE!!! todo: render queue? - return; + return false; // background os::Paint paint; @@ -352,10 +379,13 @@ void gui::redraw_window(os::Window* window, bool force_render) { } #endif + // todo: whats this do if (!window->isVisible()) window->setVisible(true); window->invalidateRegion(gfx::Region(rc)); + + return true; } void gui::on_resize(os::Window* window) { diff --git a/src/gui/gui.h b/src/gui/gui.h index befc8e6..09d7264 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -25,10 +25,10 @@ namespace gui { inline os::WindowRef window; inline bool queue_redraw = false; - void redraw_window(os::Window* window, bool force_render); + bool redraw_window(os::Window* window, bool force_render); void on_resize(os::Window* window); - void generate_messages_from_os_events(); + bool generate_messages_from_os_events(bool rendered_last); void event_loop(); void run(); diff --git a/src/gui/ui/keys.cpp b/src/gui/ui/keys.cpp new file mode 100644 index 0000000..923ef94 --- /dev/null +++ b/src/gui/ui/keys.cpp @@ -0,0 +1,19 @@ +#include "keys.h" + +void keys::on_mouse_move(gfx::Point position, os::KeyModifiers modifiers, os::PointerType pointerType, float pressure) { + mouse_pos = position; +} + +void keys::on_mouse_down(gfx::Point position, os::Event::MouseButton button, os::KeyModifiers modifiers, os::PointerType pointerType, float pressure) { + mouse_pos = position; + pressed_mouse_keys.insert(button); +} + +void keys::on_mouse_up(gfx::Point position, os::Event::MouseButton button, os::KeyModifiers modifiers, os::PointerType pointerType) { + mouse_pos = position; + pressed_mouse_keys.erase(button); +} + +bool keys::is_rect_pressed(gfx::Rect rect, os::Event::MouseButton button) { + return rect.contains(mouse_pos) && pressed_mouse_keys.contains(button); +} diff --git a/src/gui/ui/keys.h b/src/gui/ui/keys.h new file mode 100644 index 0000000..61a8d3e --- /dev/null +++ b/src/gui/ui/keys.h @@ -0,0 +1,12 @@ +#pragma once + +namespace keys { + inline gfx::Point mouse_pos; + inline std::unordered_set pressed_mouse_keys; + + void on_mouse_move(gfx::Point position, os::KeyModifiers modifiers, os::PointerType pointerType, float pressure); + void on_mouse_down(gfx::Point position, os::Event::MouseButton button, os::KeyModifiers modifiers, os::PointerType pointerType, float pressure); + void on_mouse_up(gfx::Point position, os::Event::MouseButton button, os::KeyModifiers modifiers, os::PointerType pointerType); + + bool is_rect_pressed(gfx::Rect rect, os::Event::MouseButton button); +} diff --git a/src/gui/ui/render.cpp b/src/gui/ui/render.cpp index 0e198d9..26dba80 100644 --- a/src/gui/ui/render.cpp +++ b/src/gui/ui/render.cpp @@ -12,14 +12,15 @@ void render::rect_filled(os::Surface* surface, gfx::Rect rect, gfx::Color colour surface->drawRect(rect, paint); } -void render::rounded_rect_filled(os::Surface* surface, gfx::Rect rect, gfx::Color colour, float rounding) { +void rounded_rect(os::Surface* surface, gfx::RectF rect, os::Paint paint, float rounding) { if (rect.isEmpty()) return; - os::Paint paint; - paint.color(colour); - - auto skia_rect = SkRect::MakeXYWH(SkScalar(rect.x), SkScalar(rect.y), SkScalar(rect.w), SkScalar(rect.h)); + SkRect skia_rect; + if (paint.style() == os::Paint::Style::Stroke) + skia_rect = os::to_skia_fix(rect); + else + skia_rect = os::to_skia(rect); SkRRect rrect; rrect.setRectXY(skia_rect, rounding, rounding); @@ -28,6 +29,28 @@ void render::rounded_rect_filled(os::Surface* surface, gfx::Rect rect, gfx::Colo canvas->drawRRect(rrect, paint.skPaint()); } +void render::rounded_rect_filled(os::Surface* surface, gfx::Rect rect, gfx::Color colour, float rounding) { + if (rect.isEmpty()) + return; + + os::Paint paint; + paint.color(colour); + + rounded_rect(surface, rect, paint, rounding); +} + +void render::rounded_rect_stroke(os::Surface* surface, gfx::Rect rect, gfx::Color colour, float rounding, float stroke_width) { + if (rect.isEmpty()) + return; + + os::Paint paint; + paint.color(colour); + paint.style(os::Paint::Style::Stroke); + paint.strokeWidth(stroke_width); + + rounded_rect(surface, rect, paint, rounding); +} + void render::text(os::Surface* surface, gfx::Point pos, gfx::Color colour, std::string text, const SkFont& font, os::TextAlign align) { os::Paint paint; paint.color(colour); diff --git a/src/gui/ui/render.h b/src/gui/ui/render.h index b41f6b9..2709864 100644 --- a/src/gui/ui/render.h +++ b/src/gui/ui/render.h @@ -5,6 +5,7 @@ namespace render { void rect_filled(os::Surface* surface, gfx::Rect rect, gfx::Color colour); void rounded_rect_filled(os::Surface* surface, gfx::Rect rect, gfx::Color colour, float rounding); + void rounded_rect_stroke(os::Surface* surface, gfx::Rect rect, gfx::Color colour, float rounding, float stroke_width = 1); void text(os::Surface* surface, gfx::Point pos, gfx::Color colour, std::string text, const SkFont& font, os::TextAlign align = os::TextAlign::Left); gfx::Size get_text_size(std::string text, const SkFont& font); diff --git a/src/gui/ui/ui.cpp b/src/gui/ui/ui.cpp index 008074c..c9677db 100644 --- a/src/gui/ui/ui.cpp +++ b/src/gui/ui/ui.cpp @@ -1,7 +1,8 @@ #include "ui.h" +#include "render.h" +#include "keys.h" #include "os/draw_text.h" #include "os/sampling.h" -#include "render.h" gfx::Color adjust_color(const gfx::Color& color, float anim) { return gfx::rgba(gfx::getr(color), gfx::getg(color), gfx::getb(color), round(gfx::geta(color) * anim)); // seta is broken or smth i swear @@ -115,6 +116,42 @@ void ui::render_image(os::Surface* surface, const Element* element, float anim) surface->drawRect(image_rect, stroke_paint); } +void ui::render_button(os::Surface* surface, const Element* element, float anim) { + const float button_rounding = 10.f; + + auto& button_data = std::get(element->data); + + bool hovered = element->rect.contains(keys::mouse_pos); + + render::rect_filled(surface, gfx::Rect(keys::mouse_pos - 10, gfx::Size(10, 10)), gfx::rgba(0, 0, 255, 50)); + + if (button_data.on_press) { + if (keys::is_rect_pressed(element->rect, os::Event::MouseButton::LeftButton)) { + (*button_data.on_press)(); + } + } + + gfx::Color adjusted_color = adjust_color(gfx::rgba(255, 255, 255, hovered ? 100 : 20), anim); + gfx::Color adjusted_text_color = adjust_color(gfx::rgba(255, 255, 255, 255), anim); + + gfx::Point text_pos = element->rect.center(); + text_pos.y += button_data.font.getSize() / 2 - 1; + gfx::Rect cur_rect = element->rect; + + // border + cur_rect.shrink(1); + render::rounded_rect_stroke(surface, cur_rect, adjust_color(gfx::rgba(100, 100, 100, 255), anim), button_rounding); + cur_rect.shrink(1); + render::rounded_rect_stroke(surface, cur_rect, adjust_color(gfx::rgba(50, 50, 50, 255), anim), button_rounding); + cur_rect.shrink(1); + render::rounded_rect_stroke(surface, cur_rect, adjust_color(gfx::rgba(100, 100, 100, 255), anim), button_rounding); + + // fill + render::rounded_rect_filled(surface, cur_rect, adjusted_color, button_rounding); + + render::text(surface, text_pos, adjusted_text_color, button_data.text, button_data.font, os::TextAlign::Center); +} + void ui::init_container(Container& container, gfx::Rect rect, const SkFont& font, std::optional background_color) { container.line_height = font.getSize(); container.rect = rect; @@ -260,6 +297,23 @@ std::optional> ui::add_image(const std::string& id, return element; } +std::shared_ptr ui::add_button(const std::string& id, Container& container, const std::string& text, const SkFont& font, std::optional> on_press) { + const gfx::Size button_padding(40, 20); + + gfx::Size text_size = render::get_text_size(text, font); + + auto element = std::make_shared(Element{ + ElementType::BUTTON, + gfx::Rect(container.current_position, text_size + button_padding), + ButtonElementData{ text, font, on_press }, + render_button, + }); + + add_element(container, id, element, container.line_height); + + return element; +} + void ui::center_elements_in_container(Container& container, bool horizontal, bool vertical) { int total_height = container.current_position.y - container.rect.y; diff --git a/src/gui/ui/ui.h b/src/gui/ui/ui.h index 4f9b337..5660cbd 100644 --- a/src/gui/ui/ui.h +++ b/src/gui/ui/ui.h @@ -9,7 +9,8 @@ namespace ui { enum class ElementType { BAR, TEXT, - IMAGE + IMAGE, + BUTTON }; struct BarElementData { @@ -56,7 +57,19 @@ namespace ui { } }; - using ElementData = std::variant; + struct ButtonElementData { + std::string text; + SkFont font; + std::optional> on_press; + + bool operator==(const ButtonElementData& other) const { + return text == other.text; + // Skip font comparison since it might not implement == + // also onpress function + } + }; + + using ElementData = std::variant; struct AnimationState { float speed; @@ -111,6 +124,7 @@ namespace ui { void render_bar(os::Surface* surface, const Element* element, float anim); void render_text(os::Surface* surface, const Element* element, float anim); void render_image(os::Surface* surface, const Element* element, float anim); + void render_button(os::Surface* surface, const Element* element, float anim); void init_container(Container& container, gfx::Rect rect, const SkFont& font, std::optional background_color = {}); void add_element(Container& container, const std::string& id, std::shared_ptr element, int margin_bottom); @@ -120,6 +134,7 @@ namespace ui { std::shared_ptr add_text(const std::string& id, Container& container, const std::string& text, gfx::Color color, const SkFont& font, os::TextAlign align = os::TextAlign::Left, int margin_bottom = 7); std::shared_ptr add_text_fixed(const std::string& id, Container& container, gfx::Point position, const std::string& text, gfx::Color color, const SkFont& font, os::TextAlign align = os::TextAlign::Left, int margin_bottom = 7); std::optional> add_image(const std::string& id, Container& container, std::string image_path, gfx::Size max_size, std::string image_id = ""); // use image_id to distinguish images that have the same filename and reload it (e.g. if its updated) + std::shared_ptr add_button(const std::string& id, Container& container, const std::string& text, const SkFont& font, std::optional> on_press = {}); void center_elements_in_container(Container& container, bool horizontal = true, bool vertical = true); bool update_container(os::Surface* surface, Container& container, float delta_time);