diff options
| -rw-r--r-- | app/mac/impl/file-browser-posix.cc | 10 | ||||
| -rw-r--r-- | lib/nes/app/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | lib/nes/app/action.hh (renamed from lib/nes/app/ui/action.hh) | 29 | ||||
| -rw-r--r-- | lib/nes/app/application.cc | 48 | ||||
| -rw-r--r-- | lib/nes/app/application.hh | 4 | ||||
| -rw-r--r-- | lib/nes/app/file-browser.hh | 2 | ||||
| -rw-r--r-- | lib/nes/app/graphics/renderer.cc | 2 | ||||
| -rw-r--r-- | lib/nes/app/graphics/renderer.hh | 9 | ||||
| -rw-r--r-- | lib/nes/app/ui/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | lib/nes/app/ui/screen-browser.cc | 22 | ||||
| -rw-r--r-- | lib/nes/app/ui/screen-browser.hh | 2 | ||||
| -rw-r--r-- | lib/nes/app/ui/screen-error.cc | 38 | ||||
| -rw-r--r-- | lib/nes/app/ui/screen-error.hh | 27 | ||||
| -rw-r--r-- | lib/nes/app/ui/screen.hh | 2 |
14 files changed, 158 insertions, 43 deletions
diff --git a/app/mac/impl/file-browser-posix.cc b/app/mac/impl/file-browser-posix.cc index 4c717fd..8cde6b7 100644 --- a/app/mac/impl/file-browser-posix.cc +++ b/app/mac/impl/file-browser-posix.cc @@ -3,11 +3,21 @@ #include <errno.h> #include <unistd.h> #include <fcntl.h> +#include <stdlib.h> namespace nes::app::mac { file_browser_posix::file_browser_posix() { + auto const initial_path = getenv("HOME"); + if (initial_path) + { + for (auto const component : path_view{ initial_path }) + { + if (path_.push(component) != status::success) { break; } + } + } + reopen_directory(path_.get_path()); } diff --git a/lib/nes/app/CMakeLists.txt b/lib/nes/app/CMakeLists.txt index 6e5aa93..c0d6909 100644 --- a/lib/nes/app/CMakeLists.txt +++ b/lib/nes/app/CMakeLists.txt @@ -3,6 +3,7 @@ target_sources( PRIVATE application.hh application.cc + action.hh file-browser.hh) add_subdirectory(input) diff --git a/lib/nes/app/ui/action.hh b/lib/nes/app/action.hh index 66f77f8..f63c17d 100644 --- a/lib/nes/app/ui/action.hh +++ b/lib/nes/app/action.hh @@ -1,6 +1,6 @@ #pragma once -#include "nes/common/status.hh" +#include "../common/status.hh" #include <string_view> namespace nes::app @@ -16,6 +16,7 @@ namespace nes::app go_to_browser, go_to_settings, go_to_help, + close_popup, launch_game, show_error, }; @@ -23,35 +24,41 @@ namespace nes::app private: type type_{ type::none }; std::string_view file_name_; + std::string_view message_; status error_{ status::success }; public: explicit action() = default; - static auto go_to_title() -> action { return action{ type::go_to_title, "", status::success }; } - static auto go_to_browser() -> action { return action{ type::go_to_browser, "", status::success }; } - static auto go_to_settings() -> action { return action{ type::go_to_settings, "", status::success }; } - static auto go_to_help() -> action { return action{ type::go_to_help, "", status::success }; } + static auto go_to_title() -> action { return action{ type::go_to_title }; } + static auto go_to_browser() -> action { return action{ type::go_to_browser }; } + static auto go_to_settings() -> action { return action{ type::go_to_settings }; } + static auto go_to_help() -> action { return action{ type::go_to_help }; } + static auto close_popup() -> action { return action{ type::close_popup }; } static auto launch_game(std::string_view const file_name) -> action { - return action{ type::launch_game, file_name, status::success }; + auto res = action{ type::launch_game }; + res.file_name_ = file_name; + return res; } - static auto show_error(status const error) + static auto show_error(std::string_view const message, status const error) -> action { - return action{ type::show_error, "", error }; + auto res = action{ type::show_error }; + res.message_ = message; + res.error_ = error; + return res; } auto get_type() const -> type { return type_; } auto get_file_name() const -> std::string_view { return file_name_; } + auto get_message() const -> std::string_view { return message_; } auto get_error() const -> status { return error_; } private: - explicit action(type const type, std::string_view const file_name, status const error) + explicit action(type const type) : type_{ type } - , file_name_{ file_name } - , error_{ error } { } }; diff --git a/lib/nes/app/application.cc b/lib/nes/app/application.cc index 6b7253e..ac81840 100644 --- a/lib/nes/app/application.cc +++ b/lib/nes/app/application.cc @@ -3,19 +3,25 @@ #include "nes/app/input/input-device-controller.hh" #include "nes/app/ui/screen.hh" #include "nes/app/graphics/renderer.hh" -#include "ui/action.hh" +#include "nes/app/action.hh" #include <iostream> namespace nes::app { auto application::display_proxy::switch_buffers() -> void { + auto r = renderer{ base }; + if (screen) { - auto r = renderer{ base }; screen->render(r); } + if (popup) + { + popup->render(r); + } + base.switch_buffers(); } @@ -25,13 +31,19 @@ namespace nes::app , file_browser_{ file_browser } , screen_title_{ keyboard } , screen_browser_{ keyboard, file_browser } + , screen_error_{ keyboard } { display_.screen = &screen_title_; } auto application::frame(std::chrono::microseconds const elapsed_time) -> void { - if (display_.screen) + if (display_.popup) + { + auto const a = display_.popup->process_events(); + handle_action(a); + } + else if (display_.screen) { auto const a = display_.screen->process_events(); handle_action(a); @@ -48,9 +60,12 @@ namespace nes::app if (console_->get_status() != status::success) { + // TODO: Present as error popup std::cerr << "Invalid state: " << to_string(console_->get_status()) << std::endl; std::abort(); } + + // TODO: Handle escape key } else { @@ -72,12 +87,14 @@ namespace nes::app { console_.clear(); display_.screen = &screen_title_; + display_.popup = nullptr; break; } case action::type::go_to_browser: { console_.clear(); display_.screen = &screen_browser_; + display_.popup = nullptr; break; } case action::type::go_to_settings: @@ -90,31 +107,46 @@ namespace nes::app // TODO break; } + case action::type::close_popup: + { + display_.popup = nullptr; + break; + } case action::type::launch_game: { u8 buffer[sys::cartridge::max_file_size]; auto length = u32{ 0 }; if (auto const s = file_browser_.load(a.get_file_name(), buffer, &length); s != status::success) { - std::cerr << "Unable to open cartridge file: " << to_string(console_->get_status()) << std::endl; - std::abort(); + show_error("Unable to open file", s); + break; } console_.emplace(display_, span{ buffer, length }); if (console_->get_status() != status::success) { - std::cerr << "Unable to load cartridge: " << to_string(console_->get_status()) << std::endl; - std::abort(); + show_error("Unable to load cartridge", console_->get_status()); + console_.clear(); + break; } display_.screen = nullptr; + display_.popup = nullptr; break; } case action::type::show_error: { - // TODO + show_error(a.get_message(), a.get_error()); break; } } } + + auto application::show_error(std::string_view const message, status const error) -> void + { + screen_error_.set_message(message); + screen_error_.set_error(error); + display_.popup = &screen_error_; + } + } // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/application.hh b/lib/nes/app/application.hh index 724f2b6..81e6516 100644 --- a/lib/nes/app/application.hh +++ b/lib/nes/app/application.hh @@ -3,6 +3,7 @@ #include "nes/app/input/input-manager.hh" #include "nes/app/ui/screen-title.hh" #include "nes/app/ui/screen-browser.hh" +#include "nes/app/ui/screen-error.hh" #include "nes/sys/nes.hh" #include "nes/common/box.hh" #include "nes/common/display.hh" @@ -27,6 +28,7 @@ namespace nes::app { public: display& base; + screen* popup{ nullptr }; screen* screen{ nullptr }; explicit display_proxy(display& base) @@ -45,6 +47,7 @@ namespace nes::app box<sys::nes> console_{}; screen_title screen_title_; screen_browser screen_browser_; + screen_error screen_error_; public: explicit application(display&, input_device_keyboard&, file_browser& file_browser); @@ -62,5 +65,6 @@ namespace nes::app private: auto handle_action(action) -> void; + auto show_error(std::string_view message, status error) -> void; }; } // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/file-browser.hh b/lib/nes/app/file-browser.hh index ef5343c..d956c80 100644 --- a/lib/nes/app/file-browser.hh +++ b/lib/nes/app/file-browser.hh @@ -30,7 +30,7 @@ namespace nes::app char name_[max_name_length + 1]{}; public: - item() = default; + explicit item() = default; explicit item(item_type const type, std::string_view const name) : type_{ type } diff --git a/lib/nes/app/graphics/renderer.cc b/lib/nes/app/graphics/renderer.cc index 6429a52..d512e71 100644 --- a/lib/nes/app/graphics/renderer.cc +++ b/lib/nes/app/graphics/renderer.cc @@ -129,7 +129,7 @@ namespace nes::app i32 const x, i32 const y, u32 const width, u32 const height, color const c) -> void { if (c == color::transparent) { return; } - if (x >= static_cast<i32>(width) || y >= static_cast<i32>(height)) { return; } + if (x >= static_cast<i32>(renderer::width) || y >= static_cast<i32>(renderer::height)) { return; } auto const resolved = resolve_color(c); auto const x_stop = std::min(x + static_cast<i32>(width), static_cast<i32>(renderer::width)); diff --git a/lib/nes/app/graphics/renderer.hh b/lib/nes/app/graphics/renderer.hh index cfc1889..9b764a6 100644 --- a/lib/nes/app/graphics/renderer.hh +++ b/lib/nes/app/graphics/renderer.hh @@ -20,8 +20,8 @@ namespace nes::app /// Renders tiles and other primitive shapes onto a display. /// - /// The renderer sees the display as a canvas divided into 8x8 tiles. All elements (except shadows and borders) must - /// be arranged on this 8x8 grid. + /// The renderer sees the display as a canvas divided into 8x8 tiles. All elements must be arranged on this 8x8 + /// grid. class renderer { display& display_; @@ -37,11 +37,6 @@ namespace nes::app auto operator=(renderer const&) -> renderer& = delete; auto operator=(renderer&&) -> renderer& = delete; - /// If set, add a drop shadow behind newly drawn opaque elements. - auto set_shadow(u32 x_px, u32 y_px, color) -> void; - /// If set, add an inner border inside newly drawn opaque elements. - auto set_border(u32 px, color) -> void; - /// Render a colored tile at the given tile coordinates. auto render_image_tile(i32 x, i32 y, image_tile const&) -> void; /// Render an alpha-only tile at the given tile coordinates with the given color. diff --git a/lib/nes/app/ui/CMakeLists.txt b/lib/nes/app/ui/CMakeLists.txt index 0980e97..de42744 100644 --- a/lib/nes/app/ui/CMakeLists.txt +++ b/lib/nes/app/ui/CMakeLists.txt @@ -1,10 +1,11 @@ target_sources( nes PRIVATE - action.hh selection.hh screen.hh screen-title.hh screen-title.cc screen-browser.hh - screen-browser.cc)
\ No newline at end of file + screen-browser.cc + screen-error.hh + screen-error.cc)
\ No newline at end of file diff --git a/lib/nes/app/ui/screen-browser.cc b/lib/nes/app/ui/screen-browser.cc index 14ddc23..a82e96d 100644 --- a/lib/nes/app/ui/screen-browser.cc +++ b/lib/nes/app/ui/screen-browser.cc @@ -11,13 +11,13 @@ namespace nes::app renderer& renderer, item const& item, i32 const x, i32 const y, u32 const width, color const color) const -> void { - switch (item.data.get_type()) + switch (item.base.get_type()) { case file_browser::item_type::file: renderer.render_mask_tile(x, y, tiles::icon_cartridge, color); break; case file_browser::item_type::directory: renderer.render_mask_tile(x, y, tiles::icon_directory, color); break; } - renderer.render_text(x + 2, y, item.data.get_name(), color, text_attributes{} + renderer.render_text(x + 2, y, item.base.get_name(), color, text_attributes{} .set_ellipsize_mode(ellipsize_mode::end) .set_max_width(width - 2)); } @@ -31,7 +31,7 @@ namespace nes::app if (page == 0) { items[count].is_parent = true; - items[count].data = file_browser::item{ file_browser::item_type::directory, "[Parent Directory]" }; + items[count].base = file_browser::item{ file_browser::item_type::directory, "[Parent Directory]" }; count += 1; } else @@ -48,7 +48,7 @@ namespace nes::app if (!file_browser_.read_next(&item)) { break; } items[count].is_parent = false; - items[count].data = item; + items[count].base = item; count += 1; } @@ -101,7 +101,7 @@ namespace nes::app { if (auto const s = file_browser_.navigate_up(); s != status::success) { - return action::show_error(s); + return action::show_error("Unable to navigate to directory", s); } refresh(); navigation_steps_ -= 1; @@ -120,23 +120,23 @@ namespace nes::app { if (auto const s = file_browser_.navigate_up(); s != status::success) { - return action::show_error(s); + return action::show_error("Unable to navigate to directory", s); } refresh(); if (navigation_steps_ > 0) { navigation_steps_ -= 1; } } - else if (selected->data.get_type() == file_browser::item_type::directory) + else if (selected->base.get_type() == file_browser::item_type::directory) { - if (auto const s = file_browser_.navigate(selected->data.get_name()); s != status::success) + if (auto const s = file_browser_.navigate(selected->base.get_name()); s != status::success) { - return action::show_error(s); + return action::show_error("Unable to navigate to directory", s); } refresh(); navigation_steps_ += 1; } - else if (selected->data.get_type() == file_browser::item_type::file) + else if (selected->base.get_type() == file_browser::item_type::file) { - return action::launch_game(selected->data.get_name()); + return action::launch_game(selected->base.get_name()); } } } diff --git a/lib/nes/app/ui/screen-browser.hh b/lib/nes/app/ui/screen-browser.hh index 3870f02..0ccf0b6 100644 --- a/lib/nes/app/ui/screen-browser.hh +++ b/lib/nes/app/ui/screen-browser.hh @@ -16,7 +16,7 @@ namespace nes::app struct item { bool is_parent{ false }; - file_browser::item data{ file_browser::item_type::directory, "" }; + file_browser::item base{}; }; class selection_impl final : public selection<item, page_size> diff --git a/lib/nes/app/ui/screen-error.cc b/lib/nes/app/ui/screen-error.cc new file mode 100644 index 0000000..67a3244 --- /dev/null +++ b/lib/nes/app/ui/screen-error.cc @@ -0,0 +1,38 @@ +#include "nes/app/ui/screen-error.hh" +#include "nes/app/graphics/renderer.hh" +#include "nes/app/graphics/color.hh" +#include "nes/app/input/input-device-keyboard.hh" + +namespace nes::app +{ + screen_error::screen_error(input_device_keyboard& keyboard) + : keyboard_{ keyboard } + { + } + + auto screen_error::render(renderer& renderer) -> void + { + // TODO: Nicer box + renderer.render_rect(2, 9, 28, 5, color::background_secondary); + + auto const attrs = text_attributes{} + .set_alignment(text_alignment::center) + .set_ellipsize_mode(ellipsize_mode::end) + .set_max_width(26); + renderer.render_text_format(16, 10, color::foreground_primary, attrs, "{}:", message_); + renderer.render_text(16, 12, to_string(error_), color::foreground_primary, attrs); + } + + auto screen_error::process_events() -> action + { + while (auto const event = keyboard_.poll_event()) + { + if (event == input_event::key_down(key::enter)) + { + return action::close_popup(); + } + } + + return action{}; + } +} // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/ui/screen-error.hh b/lib/nes/app/ui/screen-error.hh new file mode 100644 index 0000000..ec877e1 --- /dev/null +++ b/lib/nes/app/ui/screen-error.hh @@ -0,0 +1,27 @@ +#pragma once + +#include "nes/app/ui/screen.hh" + +namespace nes::app +{ + class input_device_keyboard; + + /// An error popup. + class screen_error final : public screen + { + input_device_keyboard& keyboard_; + std::string_view message_; + status error_{ status::success }; + + public: + explicit screen_error(input_device_keyboard&); + + auto get_message() const -> std::string_view { return message_; } + auto get_error() const -> status { return error_; } + auto set_message(std::string_view const v) { message_ = v; } + auto set_error(status const v) { error_ = v; } + + auto render(renderer&) -> void override; + auto process_events() -> action override; + }; +} // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/ui/screen.hh b/lib/nes/app/ui/screen.hh index 07e9d6f..6d30b7a 100644 --- a/lib/nes/app/ui/screen.hh +++ b/lib/nes/app/ui/screen.hh @@ -1,6 +1,6 @@ #pragma once -#include "nes/app/ui/action.hh" +#include "nes/app/action.hh" namespace nes::app { |
