diff --git a/.clang-format-ignore b/.clang-format-ignore new file mode 100644 index 0000000..1a79fe5 --- /dev/null +++ b/.clang-format-ignore @@ -0,0 +1 @@ +src/gui/resources/fonts.h diff --git a/src/gui/console.cpp b/src/gui/console.cpp deleted file mode 100644 index e226eb2..0000000 --- a/src/gui/console.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "console.h" - -#ifdef _WIN32 -void console::cleanup() { - console::close(); -} -#endif - -bool console::init() { - if (initialised) { - return false; - } - -#ifdef _WIN32 - // Windows-specific console initialization - if (!AllocConsole()) { - return false; - } - - // Register cleanup handler - atexit(cleanup); - - // Redirect stdout to the console - if (freopen_s(&stream, "CONOUT$", "w", stdout) != 0) { - FreeConsole(); - return false; - } - -#elif defined(__APPLE__) || defined(__linux__) - // Unix-like systems typically have a console by default - // Just need to check if stdout is connected to a terminal - if (!isatty(STDOUT_FILENO)) { - // If stdout is not a terminal, try to reconnect it - stream = freopen("/dev/tty", "w", stdout); - if (!stream) { - return false; - } - } - - // Configure terminal settings if needed - struct termios term; - if (tcgetattr(STDOUT_FILENO, &term) == 0) { - // Enable echo and canonical mode - term.c_lflag |= (ECHO | ICANON); - tcsetattr(STDOUT_FILENO, TCSANOW, &term); - } - - // Try to ensure the terminal is in a good state - printf("\033[0m"); // Reset all attributes - fflush(stdout); -#endif - - initialised = true; - return true; -} - -bool console::close() { - if (!initialised) { - return false; - } - -#ifdef _WIN32 - // Windows-specific console cleanup - if (stream) { - fclose(stream); - stream = nullptr; - } - - if (!FreeConsole()) { - return false; - } - -#elif defined(__APPLE__) || defined(__linux__) - // Unix-like systems: restore terminal settings - if (isatty(STDOUT_FILENO)) { - struct termios term; - if (tcgetattr(STDOUT_FILENO, &term) == 0) { - // Restore default terminal settings if needed - term.c_lflag |= (ECHO | ICANON); - tcsetattr(STDOUT_FILENO, TCSANOW, &term); - } - } - - // Close redirected stream if we created one - if (stream) { - fclose(stream); - stream = nullptr; - } -#endif - - initialised = false; - return true; -} diff --git a/src/gui/console.h b/src/gui/console.h deleted file mode 100644 index 1e76bef..0000000 --- a/src/gui/console.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -namespace console { - inline bool initialised = false; - inline FILE* stream = nullptr; - - bool init(); - bool close(); - -#ifdef _WIN32 - void cleanup(); -#endif -} diff --git a/src/gui/drag_handler.cpp b/src/gui/drag_handler.cpp new file mode 100644 index 0000000..4d399da --- /dev/null +++ b/src/gui/drag_handler.cpp @@ -0,0 +1,31 @@ + +#include "drag_handler.h" +#include "tasks.h" + +void gui::drag_handler::DragTarget::dragEnter(os::DragEvent& ev) { + // v.dropResult(os::DropOperation::None); // TODO: what does this do? is it needed? + + drag_position = ev.position(); + dragging = true; +} + +void gui::drag_handler::DragTarget::dragLeave(os::DragEvent& ev) { + // todo: not triggering on windows? + drag_position = ev.position(); + dragging = false; +} + +void gui::drag_handler::DragTarget::drag(os::DragEvent& ev) { + drag_position = ev.position(); +} + +void gui::drag_handler::DragTarget::drop(os::DragEvent& ev) { + ev.acceptDrop(true); + + if (ev.dataProvider()->contains(os::DragDataItemType::Paths)) { + std::vector paths = ev.dataProvider()->getPaths(); + tasks::add_files(paths); + } + + dragging = false; +} diff --git a/src/gui/drag_handler.h b/src/gui/drag_handler.h new file mode 100644 index 0000000..bd28ce7 --- /dev/null +++ b/src/gui/drag_handler.h @@ -0,0 +1,15 @@ + +#pragma once + +namespace gui::drag_handler { + inline bool dragging = false; + inline gfx::Point drag_position; + + class DragTarget : public os::DragTarget { + public: + void dragEnter(os::DragEvent& ev) override; + void dragLeave(os::DragEvent& ev) override; + void drag(os::DragEvent& ev) override; + void drop(os::DragEvent& ev) override; + }; +} diff --git a/src/gui/event_handler.cpp b/src/gui/event_handler.cpp new file mode 100644 index 0000000..464172e --- /dev/null +++ b/src/gui/event_handler.cpp @@ -0,0 +1,81 @@ + +#include "event_handler.h" + +#include "gui.h" + +#include "ui/keys.h" + +bool gui::event_handler::process_event(const os::Event& ev) { + switch (ev.type()) { + case os::Event::CloseApp: + case os::Event::CloseWindow: { + closing = true; + return false; + } + + case os::Event::ResizeWindow: + break; + + case os::Event::MouseMove: { + keys::on_mouse_move( + ev.position(), + ev.modifiers(), + ev.pointerType(), + ev.pressure() + ); + return true; + } + + case os::Event::MouseDown: { + 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 false; +} + +bool gui::event_handler::generate_messages_from_os_events(bool rendered_last) { // https://github.com/aseprite/aseprite/blob/45c2a5950445c884f5d732edc02989c3fb6ab1a6/src/ui/manager.cpp#L393 + bool processed_an_event = false; + + double timeout; + if (rendered_last) // an animation is playing or something, so be quick + timeout = 0.0; + else // nothing's happening so take your time - but still be fast enough for the ui to update occasionally to see if it wants to render + timeout = 1.f / 60; + + while (true) { + os::Event event; + + event_queue->getEvent(event, timeout); + + if (event.type() == os::Event::None) + break; + + if (process_event(event)) + processed_an_event = true; + + timeout = 0.0; // i think this is correct, allow blocking for first event but any subsequent one should be instant + } + + return processed_an_event; +} diff --git a/src/gui/event_handler.h b/src/gui/event_handler.h new file mode 100644 index 0000000..cf1d7aa --- /dev/null +++ b/src/gui/event_handler.h @@ -0,0 +1,7 @@ + +#pragma once + +namespace gui::event_handler { + bool process_event(const os::Event& ev); + bool generate_messages_from_os_events(bool rendered_last); +} diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 5132e72..fe8d511 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1,94 +1,11 @@ #include "gui.h" -#include "base/system_console.h" -#include "gfx/color.h" -#include "gui/ui/render.h" -#include "os/draw_text.h" -#include "os/event.h" -#include "os/event_queue.h" -#include "os/native_cursor.h" -#include "tasks.h" -#include "ui/ui.h" -#include "ui/keys.h" -#include "ui/utils.h" - -#include "resources/fonts.h" - -#define DEBUG_RENDER 1 -#define DEBUG_RENDER_LOGGING 0 - -const int font_height = 14; -const int pad_x = 24; -const int pad_y = 35; - -const float fps_smoothing = 0.95f; - -const float vsync_extra_fps = 50; -const float min_delta_time = 1.f / 10; -const float default_delta_time = 1.f / 60; -float vsync_frame_time = default_delta_time; - -bool closing = false; - -SkFont font; -SkFont header_font; -SkFont smaller_header_font; -os::EventQueue* event_queue; - -void gui::set_cursor(os::NativeCursor cursor) { - if (current_cursor != cursor) { - current_cursor = cursor; - window->setCursor(cursor); - } - - set_cursor_this_frame = true; -} - -void gui::DragTarget::dragEnter(os::DragEvent& ev) { - // v.dropResult(os::DropOperation::None); // TODO: what does this do? is it needed? - - windowData.dragPosition = ev.position(); - windowData.dragging = true; -} +#include "drag_handler.h" +#include "event_handler.h" +#include "renderer.h" +#include "window_manager.h" -void gui::DragTarget::dragLeave(os::DragEvent& ev) { - // todo: not triggering on windows? - windowData.dragPosition = ev.position(); - windowData.dragging = false; -} - -void gui::DragTarget::drag(os::DragEvent& ev) { - windowData.dragPosition = ev.position(); -} - -void gui::DragTarget::drop(os::DragEvent& ev) { - ev.acceptDrop(true); - - if (ev.dataProvider()->contains(os::DragDataItemType::Paths)) { - std::vector paths = ev.dataProvider()->getPaths(); - tasks::add_files(paths); - } - - windowData.dragging = false; -} - -static os::WindowRef create_window(os::DragTarget& dragTarget) { - auto screen = os::instance()->mainScreen(); - - os::WindowRef window = os::instance()->makeWindow(591, 381); - window->setCursor(os::NativeCursor::Arrow); - window->setTitle("Blur"); - window->setDragTarget(&dragTarget); - - gui::windowData.dropZone = gfx::Rect( - 0, - 0, - window->width(), - window->height() - ); - - return window; -} +#include "ui/utils.h" void gui::update_vsync() { #ifdef _WIN32 @@ -110,81 +27,6 @@ void gui::update_vsync() { } } -bool processEvent(const os::Event& ev) { - switch (ev.type()) { - case os::Event::CloseApp: - case os::Event::CloseWindow: { - closing = true; - return false; - } - - case os::Event::ResizeWindow: - break; - - case os::Event::MouseMove: { - keys::on_mouse_move( - ev.position(), - ev.modifiers(), - ev.pointerType(), - ev.pressure() - ); - return true; - } - - case os::Event::MouseDown: { - 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 false; -} - -bool gui::generate_messages_from_os_events(bool rendered_last) { // https://github.com/aseprite/aseprite/blob/45c2a5950445c884f5d732edc02989c3fb6ab1a6/src/ui/manager.cpp#L393 - bool processed_an_event = false; - - double timeout; - if (rendered_last) // an animation is playing or something, so be quick - timeout = 0.0; - else // nothing's happening so take your time - but still be fast enough for the ui to update occasionally to see if it wants to render - timeout = 1.f / 60; - - while (true) { - os::Event event; - - event_queue->getEvent(event, timeout); - - if (event.type() == os::Event::None) - break; - - if (processEvent(event)) - processed_an_event = true; - - timeout = 0.0; // i think this is correct, allow blocking for first event but any subsequent one should be instant - } - - return processed_an_event; -} - void gui::event_loop() { bool to_render = true; @@ -193,8 +35,8 @@ void gui::event_loop() { update_vsync(); - bool rendered = redraw_window(window.get(), to_render); // note: rendered isn't true if rendering was forced, it's only if an animation or smth is playing - to_render = generate_messages_from_os_events(rendered); // true if input handled + bool rendered = renderer::redraw_window(window.get(), to_render); // note: rendered isn't true if rendering was forced, it's only if an animation or smth is playing + to_render = event_handler::generate_messages_from_os_events(rendered); // true if input handled #if DEBUG_RENDER && DEBUG_RENDER_LOGGING printf("rendered: %d, to render: %d\n", rendered, to_render); @@ -209,236 +51,16 @@ void gui::event_loop() { } } -bool gui::redraw_window(os::Window* window, bool force_render) { - set_cursor_this_frame = false; - - auto now = std::chrono::steady_clock::now(); - static auto last_frame_time = now; - - // todo: first render in a batch might be fucked, look at progress bar skipping fully to complete instantly on 25 speed - investigate - static bool first = true; - float delta_time; - -#if DEBUG_RENDER - float fps = -1.f; -#endif - - if (first) { - delta_time = default_delta_time; - first = false; - } - else { - float time_since_last_frame = std::chrono::duration(std::chrono::steady_clock::now() - last_frame_time).count(); - -#if DEBUG_RENDER - fps = 1.f / time_since_last_frame; - -// float current_fps = 1.f / time_since_last_frame; -// if (fps == -1.f) -// fps = current_fps; -// fps = (fps * fps_smoothing) + (current_fps * (1.0f - fps_smoothing)); -#endif - - delta_time = std::min(time_since_last_frame, min_delta_time); - } - - last_frame_time = now; - - os::Surface* s = window->surface(); - const gfx::Rect rc = s->bounds(); - - 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); - force_render |= bg_overlay_shade != last_fill_shade; - - { - // const int blur_stroke_width = 1; - // const float blur_start_shade = 50; - // const float blur_screen_percent = 0.8f; - - // paint.style(os::Paint::Style::Stroke); - // paint.strokeWidth(blur_stroke_width); - - // gfx::Rect blur_drop_zone = drop_zone; - // const int blur_steps = (std::min(rc.w, rc.h) / 2.f / blur_stroke_width) * blur_screen_percent; - - // for (int i = 0; i < blur_steps; i++) { - // blur_drop_zone.shrink(blur_stroke_width); - // int shade = std::lerp(blur_start_shade, 0, ease_out_quart(i / (float)blur_steps)); - // if (shade <= 0) - // break; - - // paint.color(gfx::rgba(255, 255, 255, shade)); - // s->drawRect(blur_drop_zone, paint); - // } - } - - static ui::Container container; - - gfx::Rect container_rect = rc; - container_rect.x += pad_x; - container_rect.w -= pad_x * 2; - container_rect.y += pad_y; - container_rect.h -= pad_y * 2; - - ui::init_container(container, container_rect, font); - - static float bar_percent = 0.f; - - if (rendering.queue.empty()) { - bar_percent = 0.f; - - gfx::Point title_pos = rc.center(); - 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_button("open file button", container, "Open files", font, [] { - base::paths paths; - utils::show_file_selector("Blur input", "", {}, os::FileDialog::Type::OpenFiles, paths); - tasks::add_files(paths); - }); - ui::add_text("drop file text", container, "or drop them anywhere", gfx::rgba(255, 255, 255, 255), font, os::TextAlign::Center); - } - else { - bool is_progress_shown = false; - - for (const auto [i, render] : helpers::enumerate(rendering.queue)) { - bool current = render == rendering.current_render; - - // todo: ui concept - // screen start| [faded]last_video current_video [faded]next_video next_video2 next_video3 (+5) | screen end - // animate sliding in as it moves along the queue - ui::add_text(fmt::format("video name text {}", i), container, base::to_utf8(render->get_video_name()), gfx::rgba(255, 255, 255, (current ? 255 : 100)), smaller_header_font, os::TextAlign::Center, current ? 15 : 7); - - if (current) { - auto render_status = render->get_status(); - int bar_width = 300; - - std::string preview_path = render->get_preview_path().string(); - if (!preview_path.empty() && render_status.current_frame > 0) { - auto element = ui::add_image("preview image", container, preview_path, gfx::Size(container_rect.w, 200), std::to_string(render_status.current_frame)); - if (element) { - bar_width = (*element)->rect.w; - } - } - - if (render_status.init) { - float render_progress = render_status.current_frame / (float)render_status.total_frames; - bar_percent = std::lerp(bar_percent, render_progress, 5.f * delta_time); - - ui::add_bar("progress bar", container, bar_percent, gfx::rgba(51, 51, 51, 255), gfx::rgba(255, 255, 255, 255), bar_width, fmt::format("{:.1f}%", render_progress * 100), gfx::rgba(255, 255, 255, 255), &font); - ui::add_text("progress text", container, fmt::format("frame {}/{}", render_status.current_frame, render_status.total_frames), gfx::rgba(255, 255, 255, 155), font, os::TextAlign::Center); - ui::add_text("progress text 2", container, fmt::format("{:.2f} frames per second", render_status.fps), gfx::rgba(255, 255, 255, 155), font, os::TextAlign::Center); - - is_progress_shown = true; - } - else { - ui::add_text("initialising render text", container, "Initialising render...", gfx::rgba(255, 255, 255, 255), font, os::TextAlign::Center); - } - } - } - - if (!is_progress_shown) { - bar_percent = 0.f; // Reset when no progress bar is shown - } - } - - ui::center_elements_in_container(container); - - bool want_to_render = ui::update_container(s, container, delta_time); - if (!want_to_render && !force_render) - // note: DONT RENDER ANYTHING ABOVE HERE!!! todo: render queue? - return false; - - // background - os::Paint paint; - paint.color(gfx::rgba(0, 0, 0, 255)); - s->drawRect(rc, paint); - - if ((int)bg_overlay_shade > 0) { - paint.color(gfx::rgba(255, 255, 255, bg_overlay_shade)); - s->drawRect(drop_zone, paint); - } - -#if DEBUG_RENDER - { - // debug - static const int debug_box_size = 30; - static float x = rc.x2() - debug_box_size, y = 100.f; - static bool right = false; - static bool down = true; - x += 1.f * (right ? 1 : -1); - y += 1.f * (down ? 1 : -1); - os::Paint paint; - paint.color(gfx::rgba(255, 0, 0, 50)); - s->drawRect(gfx::Rect(x, y, debug_box_size, debug_box_size), paint); - - if (right) { - if (x + debug_box_size > rc.x2()) - right = false; - } - else { - if (x < 0) - right = true; - } - - if (down) { - if (y + debug_box_size > rc.y2()) - down = false; - } - else { - if (y < 0) - down = true; - } - } -#endif - - ui::render_container(s, container); - -#if DEBUG_RENDER - if (fps != -1.f) { - gfx::Point fps_pos( - rc.x2() - pad_x, - rc.y + pad_y - ); - render::text(s, fps_pos, gfx::rgba(0, 255, 0, 255), fmt::format("{:.0f} fps", fps), font, os::TextAlign::Right); - } -#endif - - // todo: whats this do - if (!window->isVisible()) - window->setVisible(true); - - window->invalidateRegion(gfx::Region(rc)); - - // reset cursor - if (!set_cursor_this_frame) { - set_cursor(os::NativeCursor::Arrow); - } - - return want_to_render; -} - -void gui::on_resize(os::Window* window) { - redraw_window(window, true); -} - void gui::run() { auto system = os::make_system(); - font = SkFont(); // default font - header_font = utils::create_font_from_data(EBGaramond_VariableFont_wght_ttf, EBGaramond_VariableFont_wght_ttf_len, 30); - smaller_header_font = header_font; - smaller_header_font.setSize(18.f); + renderer::init_fonts(); system->setAppMode(os::AppMode::GUI); - system->handleWindowResize = on_resize; + system->handleWindowResize = window_manager::on_resize; - DragTarget dragTarget; - window = create_window(dragTarget); + drag_handler::DragTarget drag_target; + window = window_manager::create_window(drag_target); system->finishLaunching(); system->activateApp(); @@ -446,7 +68,7 @@ void gui::run() { base::SystemConsole systemConsole; systemConsole.prepareShell(); - event_queue = system->eventQueue(); + event_queue = system->eventQueue(); // todo: move this maybe event_loop(); } diff --git a/src/gui/gui.h b/src/gui/gui.h index 2420376..64a05e3 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1,42 +1,18 @@ #pragma once namespace gui { - class DragTarget : public os::DragTarget { - public: - void dragEnter(os::DragEvent& ev) override; - void dragLeave(os::DragEvent& ev) override; - void drag(os::DragEvent& ev) override; - void drop(os::DragEvent& ev) override; - }; - - class Message {}; - - typedef std::list Messages; - static Messages msg_queue; - - struct WindowData { - bool dragging = false; - gfx::Point dragPosition; - gfx::Rect dropZone; - }; - - inline WindowData windowData; - inline os::WindowRef window; - inline bool queue_redraw = false; - - inline os::NativeCursor current_cursor; - inline bool set_cursor_this_frame = false; + inline os::EventQueue* event_queue; - void set_cursor(os::NativeCursor cursor); + inline bool closing = false; - bool redraw_window(os::Window* window, bool force_render); - void on_resize(os::Window* window); + const inline float vsync_extra_fps = 50; + const inline float min_delta_time = 1.f / 10; + const inline float default_delta_time = 1.f / 60; + inline float vsync_frame_time = default_delta_time; void update_vsync(); - bool generate_messages_from_os_events(bool rendered_last); void event_loop(); - void run(); } diff --git a/src/gui/gui_pch.h b/src/gui/gui_pch.h index 07c1b81..ce52dea 100644 --- a/src/gui/gui_pch.h +++ b/src/gui/gui_pch.h @@ -4,6 +4,7 @@ // dependencies #include "base/paths.h" +#include "base/system_console.h" #include "os/os.h" #include "os/skia/skia_helpers.h" diff --git a/src/gui/renderer.cpp b/src/gui/renderer.cpp new file mode 100644 index 0000000..f3e9a07 --- /dev/null +++ b/src/gui/renderer.cpp @@ -0,0 +1,254 @@ + +#include "renderer.h" + +#include "drag_handler.h" +#include "tasks.h" +#include "gui.h" + +#include "ui/ui.h" +#include "ui/utils.h" +#include "ui/render.h" + +#include "resources/fonts.h" + +#define DEBUG_RENDER 1 +#define DEBUG_RENDER_LOGGING 0 + +const int font_height = 14; +const int pad_x = 24; +const int pad_y = 35; + +const float fps_smoothing = 0.95f; + +void gui::renderer::init_fonts() { + fonts::font = SkFont(); // default font + fonts::header_font = utils::create_font_from_data( + EBGaramond_VariableFont_wght_ttf, + EBGaramond_VariableFont_wght_ttf_len, + 30 + ); + fonts::smaller_header_font = fonts::header_font; + fonts::smaller_header_font.setSize(18.f); +} + +void gui::renderer::set_cursor(os::NativeCursor cursor) { + if (current_cursor != cursor) { + current_cursor = cursor; + window->setCursor(cursor); + } + + set_cursor_this_frame = true; +} + +bool gui::renderer::redraw_window(os::Window* window, bool force_render) { + set_cursor_this_frame = false; + + auto now = std::chrono::steady_clock::now(); + static auto last_frame_time = now; + + // todo: first render in a batch might be fucked, look at progress bar skipping fully to complete instantly on 25 speed - investigate + static bool first = true; + float delta_time; + +#if DEBUG_RENDER + float fps = -1.f; +#endif + + if (first) { + delta_time = default_delta_time; + first = false; + } + else { + float time_since_last_frame = std::chrono::duration(std::chrono::steady_clock::now() - last_frame_time).count(); + +#if DEBUG_RENDER + fps = 1.f / time_since_last_frame; + +// float current_fps = 1.f / time_since_last_frame; +// if (fps == -1.f) +// fps = current_fps; +// fps = (fps * fps_smoothing) + (current_fps * (1.0f - fps_smoothing)); +#endif + + delta_time = std::min(time_since_last_frame, min_delta_time); + } + + last_frame_time = now; + + os::Surface* s = window->surface(); + const gfx::Rect rc = s->bounds(); + + 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, drag_handler::dragging ? 30.f : 0.f, 25.f * delta_time); + force_render |= bg_overlay_shade != last_fill_shade; + + { + // const int blur_stroke_width = 1; + // const float blur_start_shade = 50; + // const float blur_screen_percent = 0.8f; + + // paint.style(os::Paint::Style::Stroke); + // paint.strokeWidth(blur_stroke_width); + + // gfx::Rect blur_drop_zone = drop_zone; + // const int blur_steps = (std::min(rc.w, rc.h) / 2.f / blur_stroke_width) * blur_screen_percent; + + // for (int i = 0; i < blur_steps; i++) { + // blur_drop_zone.shrink(blur_stroke_width); + // int shade = std::lerp(blur_start_shade, 0, ease_out_quart(i / (float)blur_steps)); + // if (shade <= 0) + // break; + + // paint.color(gfx::rgba(255, 255, 255, shade)); + // s->drawRect(blur_drop_zone, paint); + // } + } + + static ui::Container container; + + gfx::Rect container_rect = rc; + container_rect.x += pad_x; + container_rect.w -= pad_x * 2; + container_rect.y += pad_y; + container_rect.h -= pad_y * 2; + + ui::init_container(container, container_rect, fonts::font); + + static float bar_percent = 0.f; + + if (rendering.queue.empty()) { + bar_percent = 0.f; + + gfx::Point title_pos = rc.center(); + title_pos.y = pad_y + fonts::header_font.getSize(); + + ui::add_text_fixed("blur title text", container, title_pos, "blur", gfx::rgba(255, 255, 255, 255), fonts::header_font, os::TextAlign::Center, 15); + ui::add_button("open file button", container, "Open files", fonts::font, [] { + base::paths paths; + utils::show_file_selector("Blur input", "", {}, os::FileDialog::Type::OpenFiles, paths); + tasks::add_files(paths); + }); + ui::add_text("drop file text", container, "or drop them anywhere", gfx::rgba(255, 255, 255, 255), fonts::font, os::TextAlign::Center); + } + else { + bool is_progress_shown = false; + + for (const auto [i, render] : helpers::enumerate(rendering.queue)) { + bool current = render == rendering.current_render; + + // todo: ui concept + // screen start| [faded]last_video current_video [faded]next_video next_video2 next_video3 (+5) | screen end + // animate sliding in as it moves along the queue + ui::add_text(fmt::format("video name text {}", i), container, base::to_utf8(render->get_video_name()), gfx::rgba(255, 255, 255, (current ? 255 : 100)), fonts::smaller_header_font, os::TextAlign::Center, current ? 15 : 7); + + if (current) { + auto render_status = render->get_status(); + int bar_width = 300; + + std::string preview_path = render->get_preview_path().string(); + if (!preview_path.empty() && render_status.current_frame > 0) { + auto element = ui::add_image("preview image", container, preview_path, gfx::Size(container_rect.w, 200), std::to_string(render_status.current_frame)); + if (element) { + bar_width = (*element)->rect.w; + } + } + + if (render_status.init) { + float render_progress = render_status.current_frame / (float)render_status.total_frames; + bar_percent = std::lerp(bar_percent, render_progress, 5.f * delta_time); + + ui::add_bar("progress bar", container, bar_percent, gfx::rgba(51, 51, 51, 255), gfx::rgba(255, 255, 255, 255), bar_width, fmt::format("{:.1f}%", render_progress * 100), gfx::rgba(255, 255, 255, 255), &fonts::font); + ui::add_text("progress text", container, fmt::format("frame {}/{}", render_status.current_frame, render_status.total_frames), gfx::rgba(255, 255, 255, 155), fonts::font, os::TextAlign::Center); + ui::add_text("progress text 2", container, fmt::format("{:.2f} frames per second", render_status.fps), gfx::rgba(255, 255, 255, 155), fonts::font, os::TextAlign::Center); + + is_progress_shown = true; + } + else { + ui::add_text("initialising render text", container, "Initialising render...", gfx::rgba(255, 255, 255, 255), fonts::font, os::TextAlign::Center); + } + } + } + + if (!is_progress_shown) { + bar_percent = 0.f; // Reset when no progress bar is shown + } + } + + ui::center_elements_in_container(container); + + bool want_to_render = ui::update_container(s, container, delta_time); + if (!want_to_render && !force_render) + // note: DONT RENDER ANYTHING ABOVE HERE!!! todo: render queue? + return false; + + // background + os::Paint paint; + paint.color(gfx::rgba(0, 0, 0, 255)); + s->drawRect(rc, paint); + + if ((int)bg_overlay_shade > 0) { + paint.color(gfx::rgba(255, 255, 255, bg_overlay_shade)); + s->drawRect(drop_zone, paint); + } + +#if DEBUG_RENDER + { + // debug + static const int debug_box_size = 30; + static float x = rc.x2() - debug_box_size, y = 100.f; + static bool right = false; + static bool down = true; + x += 1.f * (right ? 1 : -1); + y += 1.f * (down ? 1 : -1); + os::Paint paint; + paint.color(gfx::rgba(255, 0, 0, 50)); + s->drawRect(gfx::Rect(x, y, debug_box_size, debug_box_size), paint); + + if (right) { + if (x + debug_box_size > rc.x2()) + right = false; + } + else { + if (x < 0) + right = true; + } + + if (down) { + if (y + debug_box_size > rc.y2()) + down = false; + } + else { + if (y < 0) + down = true; + } + } +#endif + + ui::render_container(s, container); + +#if DEBUG_RENDER + if (fps != -1.f) { + gfx::Point fps_pos( + rc.x2() - pad_x, + rc.y + pad_y + ); + render::text(s, fps_pos, gfx::rgba(0, 255, 0, 255), fmt::format("{:.0f} fps", fps), fonts::font, os::TextAlign::Right); + } +#endif + + // todo: whats this do + if (!window->isVisible()) + window->setVisible(true); + + window->invalidateRegion(gfx::Region(rc)); + + // reset cursor + if (!set_cursor_this_frame) { + set_cursor(os::NativeCursor::Arrow); + } + + return want_to_render; +} diff --git a/src/gui/renderer.h b/src/gui/renderer.h new file mode 100644 index 0000000..6ec95e9 --- /dev/null +++ b/src/gui/renderer.h @@ -0,0 +1,19 @@ + +#pragma once + +namespace gui::renderer { + namespace fonts { + inline SkFont font; + inline SkFont header_font; + inline SkFont smaller_header_font; + } + + inline os::NativeCursor current_cursor; + inline bool set_cursor_this_frame = false; + + void init_fonts(); + + void set_cursor(os::NativeCursor cursor); + + bool redraw_window(os::Window* window, bool force_render); +} diff --git a/src/gui/resources/fonts.h b/src/gui/resources/fonts.h index 677bd07..23d19a1 100644 --- a/src/gui/resources/fonts.h +++ b/src/gui/resources/fonts.h @@ -1,7 +1,8 @@ -const size_t EBGaramond_VariableFont_wght_ttf_len = 936656; +#pragma once -// clang-format off -unsigned char EBGaramond_VariableFont_wght_ttf[] = { +const inline size_t EBGaramond_VariableFont_wght_ttf_len = 936656; + +const inline unsigned char EBGaramond_VariableFont_wght_ttf[] = { 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x04, 0x00, 0x40, 0x47, 0x44, 0x45, 0x46, 0x94, 0xaf, 0x92, 0x57, 0x00, 0x00, 0x03, 0x34, 0x00, 0x00, 0x05, 0x60, 0x47, 0x50, 0x4f, 0x53, 0x58, 0x97, 0x67, 0xdd, @@ -78058,7 +78059,6 @@ unsigned char EBGaramond_VariableFont_wght_ttf[] = { 0x00, 0xff, 0xff, 0xff, 0xec, 0xfe, 0xe1, 0x00, 0xa6, 0x01, 0xb2, 0x02, 0x06, 0x03, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00 }; -// clang-format on // const size_t ttf_FiraCode_Regular_len = 289624; diff --git a/src/gui/tasks.cpp b/src/gui/tasks.cpp index 7047483..7537d34 100644 --- a/src/gui/tasks.cpp +++ b/src/gui/tasks.cpp @@ -5,10 +5,9 @@ void tasks::run() { blur.initialise(false, true); rendering.progress_callback = [] { - gui::queue_redraw = true; - if (gui::window) { // idk what you're supposed to do to trigger a redraw in a separate thread!!! I dont do gui!!! this works tho : ) + // todo: revisit this os::Event event; gui::window->queueEvent(event); } diff --git a/src/gui/ui/elements/button.cpp b/src/gui/ui/elements/button.cpp index 38a7a21..ddc4194 100644 --- a/src/gui/ui/elements/button.cpp +++ b/src/gui/ui/elements/button.cpp @@ -2,7 +2,8 @@ #include "../render.h" #include "../utils.h" #include "../keys.h" -#include "../../gui.h" + +#include "../../renderer.h" void ui::render_button(os::Surface* surface, const Element* element, float anim) { const float button_rounding = 7.8f; @@ -11,7 +12,7 @@ void ui::render_button(os::Surface* surface, const Element* element, float anim) bool hovered = element->rect.contains(keys::mouse_pos); if (hovered) { - gui::set_cursor(os::NativeCursor::Link); + gui::renderer::set_cursor(os::NativeCursor::Link); if (button_data.on_press) { if (keys::is_mouse_down()) { diff --git a/src/gui/window_manager.cpp b/src/gui/window_manager.cpp new file mode 100644 index 0000000..8edc62f --- /dev/null +++ b/src/gui/window_manager.cpp @@ -0,0 +1,19 @@ + +#include "window_manager.h" + +#include "renderer.h" + +os::WindowRef gui::window_manager::create_window(os::DragTarget& dragTarget) { + auto screen = os::instance()->mainScreen(); + + os::WindowRef window = os::instance()->makeWindow(591, 381); + window->setCursor(os::NativeCursor::Arrow); + window->setTitle("Blur"); + window->setDragTarget(&dragTarget); + + return window; +} + +void gui::window_manager::on_resize(os::Window* window) { + renderer::redraw_window(window, true); +} diff --git a/src/gui/window_manager.h b/src/gui/window_manager.h new file mode 100644 index 0000000..6589422 --- /dev/null +++ b/src/gui/window_manager.h @@ -0,0 +1,8 @@ + +#pragma once + +namespace gui::window_manager { + os::WindowRef create_window(os::DragTarget& dragTarget); + + void on_resize(os::Window* window); +}