diff options
| author | Hannes Schulze <hannes.hs.schulze@fau.de> | 2025-05-02 16:17:56 +0200 |
|---|---|---|
| committer | Hannes Schulze <hannes.hs.schulze@fau.de> | 2025-05-02 16:17:56 +0200 |
| commit | 850159dc9663a1cb0a7bb8f25fe1d426134ccabb (patch) | |
| tree | 122c61166f6589be1e15ef328388cbc2596f97f4 | |
| parent | 91c9b3addf5f3db972fd28752855eda811221afe (diff) | |
implement file viewer
| -rw-r--r-- | lib/nes/app/application.cc | 10 | ||||
| -rw-r--r-- | lib/nes/app/application.hh | 2 | ||||
| -rw-r--r-- | lib/nes/app/ui/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | lib/nes/app/ui/screen-file-viewer.cc | 96 | ||||
| -rw-r--r-- | lib/nes/app/ui/screen-file-viewer.hh | 34 | ||||
| -rw-r--r-- | lib/nes/common/containers/span.hh | 8 |
6 files changed, 151 insertions, 3 deletions
diff --git a/lib/nes/app/application.cc b/lib/nes/app/application.cc index 521cc40..622fdf7 100644 --- a/lib/nes/app/application.cc +++ b/lib/nes/app/application.cc @@ -1,6 +1,5 @@ #include "nes/app/application.hh" #include "nes/app/input/input-device-keyboard.hh" -#include "nes/app/input/input-device-controller.hh" #include "nes/app/ui/screen.hh" #include "nes/app/graphics/renderer.hh" #include "nes/app/action.hh" @@ -57,6 +56,7 @@ namespace nes::app , screen_error_{ keyboard } , screen_confirm_quit_{ keyboard } , screen_prompt_key_{ keyboard } + , screen_file_viewer_{ keyboard, file_browser } { display_.visible_screen = &screen_title_; } @@ -193,6 +193,14 @@ namespace nes::app } case action::type::view_file: { + if (auto const s = screen_file_viewer_.load(a.get_file_name()); s != status::success) + { + show_error("Unable to open file", s); + } + else + { + display_.visible_popup = &screen_file_viewer_; + } break; } case action::type::show_error: diff --git a/lib/nes/app/application.hh b/lib/nes/app/application.hh index 5ed7413..cee99ed 100644 --- a/lib/nes/app/application.hh +++ b/lib/nes/app/application.hh @@ -8,6 +8,7 @@ #include "nes/app/ui/screen-error.hh" #include "nes/app/ui/screen-confirm-quit.hh" #include "nes/app/ui/screen-prompt-key.hh" +#include "nes/app/ui/screen-file-viewer.hh" #include "nes/app/preferences.hh" #include "nes/sys/nes.hh" #include "nes/common/containers/box.hh" @@ -66,6 +67,7 @@ namespace nes::app screen_error screen_error_; screen_confirm_quit screen_confirm_quit_; screen_prompt_key screen_prompt_key_; + screen_file_viewer screen_file_viewer_; public: explicit application(display&, input_device_keyboard&, file_browser&); diff --git a/lib/nes/app/ui/CMakeLists.txt b/lib/nes/app/ui/CMakeLists.txt index 1f8d138..5bdad70 100644 --- a/lib/nes/app/ui/CMakeLists.txt +++ b/lib/nes/app/ui/CMakeLists.txt @@ -16,4 +16,6 @@ target_sources( screen-confirm-quit.hh screen-confirm-quit.cc screen-prompt-key.hh - screen-prompt-key.cc)
\ No newline at end of file + screen-prompt-key.cc + screen-file-viewer.hh + screen-file-viewer.cc) diff --git a/lib/nes/app/ui/screen-file-viewer.cc b/lib/nes/app/ui/screen-file-viewer.cc new file mode 100644 index 0000000..b7551ba --- /dev/null +++ b/lib/nes/app/ui/screen-file-viewer.cc @@ -0,0 +1,96 @@ +#include "nes/app/ui/screen-file-viewer.hh" +#include "nes/app/file-browser.hh" +#include "nes/app/graphics/renderer.hh" +#include "nes/app/graphics/color.hh" +#include "nes/app/input/input-device-keyboard.hh" +#include "nes/common/containers/span.hh" + +namespace nes::app +{ + screen_file_viewer::screen_file_viewer(input_device_keyboard& keyboard, file_browser& file_browser) + : keyboard_{ keyboard } + , file_browser_{ file_browser } + { + } + + auto screen_file_viewer::load(string_view const file_name) -> status + { + auto length = u32{ 0 }; + if (auto const s = file_browser_.load(file_name, buffer_, &length); s != status::success) { return s; } + + // Do some magical text wrapping + auto const data = span{ buffer_ }.subspan(0, length); + auto const src = string_view{ data.unsafe_bitcast<char const>() }; + line_count_ = 0; + auto line_pos = u32{ 0 }; + auto line_start = u32{ 0 }; + auto const end_line = [&](bool const force) + { + auto const l = src.substring(line_start, line_pos); + if (l.is_empty() && !force) { return; } + + if (line_count_ < max_line_count) + { + lines_[line_count_] = l; + line_count_ += 1; + } + line_pos = 0; + }; + for (auto i = u32{ 0 }; i < src.get_length(); ++i) + { + auto const c = src[i]; + if (c == '\n' || line_pos == line_length) + { + end_line(c == '\n'); + line_pos = 0; + line_start = i + 1; + } + else if (c == '\r') + { + // ignore + } + else + { + line_pos += 1; + } + } + end_line(false); + + return status::success; + } + + auto screen_file_viewer::render(renderer& renderer) -> void + { + auto const width = line_length + 4; + auto const height = line_count_ + 7; + auto const y = (renderer::height - height) / 2 + 2; + renderer.render_rect(2, y, width, height, color::background_secondary); + renderer.render_border(2, y, width, height, color::foreground_primary); + + auto const attrs = text_attributes{} + .set_alignment(text_alignment::left) + .set_max_width(line_length); + for (auto i = u32{ 0 }; i < line_count_; ++i) + { + renderer.render_text(4, y + 2 + i, lines_[i], color::foreground_primary, attrs); + } + auto const button_attrs = text_attributes{} + .set_alignment(text_alignment::center) + .set_ellipsize_mode(ellipsize_mode::end) + .set_max_width(26); + renderer.render_text(16, y + height - 3, "> Close <", color::accent_primary, button_attrs); + } + + auto screen_file_viewer::process_events() -> action + { + while (auto const event = keyboard_.poll_event()) + { + if (event == input_event::key_down(key::escape) || event == input_event::key_down(key::enter)) + { + return action::close_popup(); + } + } + + return action{}; + } +} // namespace nes::app diff --git a/lib/nes/app/ui/screen-file-viewer.hh b/lib/nes/app/ui/screen-file-viewer.hh new file mode 100644 index 0000000..ed01e6f --- /dev/null +++ b/lib/nes/app/ui/screen-file-viewer.hh @@ -0,0 +1,34 @@ +#pragma once + +#include "nes/common/types.hh" +#include "nes/common/containers/string-view.hh" +#include "nes/app/ui/screen.hh" +#include "nes/common/containers/string-view.hh" + +namespace nes::app +{ + class input_device_keyboard; + class file_browser; + + /// An popup showing the contents of a file. + class screen_file_viewer final : public screen + { + static constexpr auto line_length = u32{ 24 }; + static constexpr auto buffer_length = u32{ 1024 }; + static constexpr auto max_line_count = u32{ 16 }; + + input_device_keyboard& keyboard_; + file_browser& file_browser_; + u8 buffer_[buffer_length]; + string_view lines_[max_line_count]; + u32 line_count_{ 0 }; + + public: + explicit screen_file_viewer(input_device_keyboard&, file_browser&); + + auto load(string_view file_name) -> status; + + auto render(renderer&) -> void override; + auto process_events() -> action override; + }; +} // namespace nes::app diff --git a/lib/nes/common/containers/span.hh b/lib/nes/common/containers/span.hh index 324d486..ec48622 100644 --- a/lib/nes/common/containers/span.hh +++ b/lib/nes/common/containers/span.hh @@ -85,6 +85,12 @@ namespace nes return span<T, OtherLength>{ data_ + first, OtherLength, {} }; } + template<typename U> + auto unsafe_bitcast() const -> span<U> + { + return span<U>{ reinterpret_cast<U*>(data_), static_cast<u32>(length_ / sizeof(U)) }; + } + auto operator[](u32 const index) const -> T& { NES_ASSERT(index < get_length() && "index out of bounds"); @@ -98,4 +104,4 @@ namespace nes { } }; -} // namespace nes
\ No newline at end of file +} // namespace nes |
