summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHannes Schulze <hannes.hs.schulze@fau.de>2025-05-02 16:17:56 +0200
committerHannes Schulze <hannes.hs.schulze@fau.de>2025-05-02 16:17:56 +0200
commit850159dc9663a1cb0a7bb8f25fe1d426134ccabb (patch)
tree122c61166f6589be1e15ef328388cbc2596f97f4
parent91c9b3addf5f3db972fd28752855eda811221afe (diff)
implement file viewer
-rw-r--r--lib/nes/app/application.cc10
-rw-r--r--lib/nes/app/application.hh2
-rw-r--r--lib/nes/app/ui/CMakeLists.txt4
-rw-r--r--lib/nes/app/ui/screen-file-viewer.cc96
-rw-r--r--lib/nes/app/ui/screen-file-viewer.hh34
-rw-r--r--lib/nes/common/containers/span.hh8
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