feat(screenshot-ui): add newest/oldest sort selector

Add a combo button to the screenshot gallery to sort files by modification time (newest or oldest first). Store timestamp per image and rebuild layout when sort order changes. Include translations for en_US and zh_CN.
This commit is contained in:
2026-08-07 20:22:50 +08:00
parent d7cb178c2b
commit d9b4b42f74
4 changed files with 83 additions and 2 deletions

View File

@@ -49,5 +49,8 @@
"item.water.name": "water", "item.water.name": "water",
"item.pig_spawn_egg.name": "pig spawn egg", "item.pig_spawn_egg.name": "pig spawn egg",
"screenshot.no_screenshot": "No Screenshot", "screenshot.no_screenshot": "No Screenshot",
"button.show_in_file_manager": "Show In File Manager" "button.show_in_file_manager": "Show In File Manager",
"file.sort_type": "File Sort Type: {type}",
"file.newest_first": "Newest First",
"file.oldest_first": "Oldest First"
} }

View File

@@ -49,5 +49,8 @@
"item.water.name": "水", "item.water.name": "水",
"item.pig_spawn_egg.name": "猪猪刷怪蛋", "item.pig_spawn_egg.name": "猪猪刷怪蛋",
"screenshot.no_screenshot": "没有截图", "screenshot.no_screenshot": "没有截图",
"button.show_in_file_manager": "在文件管理器中显示" "button.show_in_file_manager": "在文件管理器中显示",
"file.sort_type": "文件排序方式:{type}",
"file.newest_first": "最新在前",
"file.oldest_first": "最旧在前"
} }

View File

@@ -2,6 +2,8 @@
#include "Cubed/ui/image.hpp" #include "Cubed/ui/image.hpp"
#include "Cubed/ui/ui_manager.hpp" #include "Cubed/ui/ui_manager.hpp"
#include <chrono>
namespace Cubed { namespace Cubed {
class Texture; class Texture;
class ScreenshotScene; class ScreenshotScene;
@@ -20,16 +22,22 @@ public:
void update_layout(int width, int height); void update_layout(int width, int height);
void open_lightbox(const Texture* image); void open_lightbox(const Texture* image);
void update(float dt) override;
private: private:
struct ImageInfo { struct ImageInfo {
const Texture* texture = nullptr; const Texture* texture = nullptr;
int width = 0; int width = 0;
int height = 0; int height = 0;
std::chrono::nanoseconds time{0};
}; };
enum class SortType { NEWEST_FIRST, OLDEST_FIRST };
ScreenshotScene& m_scene; ScreenshotScene& m_scene;
std::vector<ImageInfo> m_image_infos; std::vector<ImageInfo> m_image_infos;
SortType m_sort_type = SortType::NEWEST_FIRST;
bool m_need_rebuild = false;
Widget* m_lightbox = nullptr; Widget* m_lightbox = nullptr;
Image* m_lightbox_image = nullptr; Image* m_lightbox_image = nullptr;
bool handle_window_resize_event(const WindowResizeEvent& e) override; bool handle_window_resize_event(const WindowResizeEvent& e) override;

View File

@@ -7,12 +7,14 @@
#include "Cubed/tools/file_utils.hpp" #include "Cubed/tools/file_utils.hpp"
#include "Cubed/ui/button.hpp" #include "Cubed/ui/button.hpp"
#include "Cubed/ui/column_layout.hpp" #include "Cubed/ui/column_layout.hpp"
#include "Cubed/ui/combo_button.hpp"
#include "Cubed/ui/row_layout.hpp" #include "Cubed/ui/row_layout.hpp"
#include "Cubed/ui/scroll_view.hpp" #include "Cubed/ui/scroll_view.hpp"
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace { namespace {
constexpr std::array<std::string_view, 4> IMAGE_EXT{".jpg", ".jpeg", ".png", constexpr std::array<std::string_view, 4> IMAGE_EXT{".jpg", ".jpeg", ".png",
@@ -61,6 +63,17 @@ void ScreenshotUI::update_layout(int width, int height) {
info.texture->set_linear(); info.texture->set_linear();
info.height = info.texture->height() * IMAGE_SCALE; info.height = info.texture->height() * IMAGE_SCALE;
info.width = info.texture->width() * IMAGE_SCALE; info.width = info.texture->width() * IMAGE_SCALE;
std::error_code ec;
auto ftime = fs::last_write_time(entry, ec);
if (!ec) {
auto sys_time =
std::chrono::clock_cast<std::chrono::system_clock>(ftime);
info.time =
std::chrono::time_point_cast<std::chrono::nanoseconds>(
sys_time)
.time_since_epoch();
}
m_image_infos.emplace_back(std::move(info)); m_image_infos.emplace_back(std::move(info));
} }
constexpr int PADDING = 50; constexpr int PADDING = 50;
@@ -88,6 +101,18 @@ void ScreenshotUI::update_layout(int width, int height) {
row->set_spacing(SPACING); row->set_spacing(SPACING);
}; };
std::sort(m_image_infos.begin(), m_image_infos.end(),
[this](const ImageInfo& a, const ImageInfo& b) {
switch (m_sort_type) {
case SortType::NEWEST_FIRST:
return a.time > b.time;
case SortType::OLDEST_FIRST:
return a.time < b.time;
default:
return a.time > b.time;
}
});
for (auto& image : m_image_infos) { for (auto& image : m_image_infos) {
if (!row) { if (!row) {
create_row(); create_row();
@@ -111,14 +136,47 @@ void ScreenshotUI::update_layout(int width, int height) {
.set_color(Color::WHITE) .set_color(Color::WHITE)
.set_anchor(Anchor::CENTER); .set_anchor(Anchor::CENTER);
} }
{
auto& combo = bg->add_child<ComboButton>();
combo.set_anchor(Anchor::TOP_RIGHT);
combo.set_offset(title.get_offset());
switch (m_sort_type) {
case SortType::NEWEST_FIRST:
combo.set_index(0);
break;
case SortType::OLDEST_FIRST:
combo.set_index(1);
break;
default:
combo.set_index(0);
}
combo.set_combo_text("file.sort_type", "type");
std::vector<ComboPair> combos;
combos.emplace_back(tr("file.newest_first"), [this]() {
m_sort_type = SortType::NEWEST_FIRST;
m_need_rebuild = true;
});
combos.emplace_back(tr("file.oldest_first"), [this]() {
m_sort_type = SortType::OLDEST_FIRST;
m_need_rebuild = true;
});
combo.set_default_image(texture_manager);
combo.set_combos(combos);
}
auto& bottom_row = bg->add_child<RowLayout>(); auto& bottom_row = bg->add_child<RowLayout>();
bottom_row.set_spacing(10); bottom_row.set_spacing(10);
bottom_row.set_anchor(Anchor::BOTTOM_CENTER).set_offset({0, -30}); bottom_row.set_anchor(Anchor::BOTTOM_CENTER).set_offset({0, -30});
auto& open_file = bottom_row.add_child<Button>(); auto& open_file = bottom_row.add_child<Button>();
open_file.set_default_image(texture_manager) open_file.set_default_image(texture_manager)
.set_text(tr("button.show_in_file_manager")) .set_text(tr("button.show_in_file_manager"))
.set_clicked( .set_clicked(
[]() { Tools::open_file_manager(Renderer::SCREENSHOT_PATH); }); []() { Tools::open_file_manager(Renderer::SCREENSHOT_PATH); });
auto& return_button = bottom_row.add_child<Button>(); auto& return_button = bottom_row.add_child<Button>();
return_button.set_default_image(texture_manager); return_button.set_default_image(texture_manager);
return_button.set_text(tr("button.return")); return_button.set_text(tr("button.return"));
@@ -164,6 +222,15 @@ void ScreenshotUI::open_lightbox(const Texture* image) {
m_lightbox->set_visible(true); m_lightbox->set_visible(true);
} }
void ScreenshotUI::update(float dt) {
if (m_need_rebuild) {
m_need_rebuild = false;
update_layout(Widget::get_window_width(), Widget::get_window_height());
}
UIManager::update(dt);
}
bool ScreenshotUI::handle_window_resize_event(const WindowResizeEvent& e) { bool ScreenshotUI::handle_window_resize_event(const WindowResizeEvent& e) {
update_layout(e.width, e.height); update_layout(e.width, e.height);
return UIManager::handle_window_resize_event(e); return UIManager::handle_window_resize_event(e);