Add cat support
Some checks failed
Build / build-gentoo (push) Failing after 1m11s
Build / download-system-deps (push) Successful in 3m44s
Build / get-source-code (push) Successful in 13m23s
Build / build-deb (push) Successful in 11m40s
Build / build-appimage (push) Successful in 4m53s
Build / build-android (push) Failing after 3m19s
Build / build-windows (push) Failing after 8m9s
Some checks failed
Build / build-gentoo (push) Failing after 1m11s
Build / download-system-deps (push) Successful in 3m44s
Build / get-source-code (push) Successful in 13m23s
Build / build-deb (push) Successful in 11m40s
Build / build-appimage (push) Successful in 4m53s
Build / build-android (push) Failing after 3m19s
Build / build-windows (push) Failing after 8m9s
This commit is contained in:
parent
d6f440c8d4
commit
9899794b81
18 changed files with 499 additions and 22 deletions
|
@ -44,7 +44,7 @@ def add_font(input: str, output: str|None = None):
|
|||
def add_graphic(input: str):
|
||||
GRAPHIC=path.basename(input).removesuffix(".png").lower().replace('-', '_')
|
||||
print("Adding graphic '%s' from file '%s'" % (GRAPHIC, input))
|
||||
add_base85(input, GRAPHIC)
|
||||
add_basic(input, GRAPHIC)
|
||||
def add_license(input: str, output: str):
|
||||
LICENSE = output + "_license"
|
||||
print("Adding license '%s' (C identifier: '%s') from file '%s'" % (output, LICENSE, input))
|
||||
|
@ -72,6 +72,7 @@ for i in glob("Noto_Sans_JP/*.ttf"):
|
|||
add_font(i)
|
||||
add_font("ForkAwesome/fonts/forkawesome-webfont.ttf", "forkawesome")
|
||||
add_graphic("icon.png")
|
||||
add_graphic("catoc.png")
|
||||
add_license("Noto_Sans/OFL.txt", "notosans")
|
||||
add_license("Noto_Sans_JP/OFL.txt", "notosansjp")
|
||||
add_license("../LICENSE.MIT", "looper_mit")
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
#include <LayoutItem.h>
|
||||
#include <TranslationUtils.h>
|
||||
#include <SDL.h>
|
||||
#include <cats.hpp>
|
||||
#include "icons.h"
|
||||
#include "utils.h"
|
||||
#include "aboutwindow.h"
|
||||
|
@ -61,6 +63,22 @@ void HaikuLooperWindow::UpdateViewFlags(BLayout *layout) {
|
|||
}
|
||||
UpdateViewFlags(owner);
|
||||
}
|
||||
void HaikuLooperWindow::AddCat(std::string name, BBitmap *bitmap) {
|
||||
if (bitmap == NULL) return;
|
||||
if (cats.contains(name)) {
|
||||
delete cats[name];
|
||||
}
|
||||
cats[name] = bitmap;
|
||||
}
|
||||
void HaikuLooperWindow::LoadCat(const char *path) {
|
||||
return BTranslationUtils::LoadBitmap(path);
|
||||
}
|
||||
void HaikuLooperWindow::LoadCat(const void *ptr, size_t len, const char *name) {
|
||||
BMemoryIO *io = new BMemoryIO(ptr, len);
|
||||
BBitmap *output = BTranslationUtils::LoadBitmap(io);
|
||||
delete io;
|
||||
return output;
|
||||
}
|
||||
HaikuLooperWindow::HaikuLooperWindow(Playback *playback) : BWindow(BRect(100, 100, 500, 100), "Looper", B_TITLED_WINDOW, 0) {
|
||||
pause_bitmap = load_icon(ICON_PAUSE);
|
||||
resume_bitmap = load_icon(ICON_PLAY);
|
||||
|
@ -94,7 +112,7 @@ HaikuLooperWindow::HaikuLooperWindow(Playback *playback) : BWindow(BRect(100, 10
|
|||
menu_bar->AddItem(file_menu);
|
||||
menu_bar->AddItem(help_menu);
|
||||
layout->AddView(menu_bar);
|
||||
BView *spacer = new BView("spacer", B_SUPPORTS_LAYOUT|B_FRAME_EVENTS);
|
||||
spacer = new BView("spacer", B_SUPPORTS_LAYOUT|B_FRAME_EVENTS);
|
||||
spacer->SetExplicitPreferredSize(BSize(0, 0));
|
||||
spacer->SetExplicitMinSize(BSize(0, 0));
|
||||
layout->AddView(spacer);
|
||||
|
@ -161,6 +179,30 @@ HaikuLooperWindow::HaikuLooperWindow(Playback *playback) : BWindow(BRect(100, 10
|
|||
MessageReceived(msg);
|
||||
delete msg;
|
||||
}
|
||||
void HaikuLooperWindow::UpdateCat(BBitmap *cat) {
|
||||
if (cat != NULL) {
|
||||
auto bounds = cat->Bounds();
|
||||
auto w = bounds.Width(), h = bounds.Height();
|
||||
auto sb = spacer->Bounds();
|
||||
auto sw = sb.Width(), sh = sb.Height();
|
||||
BRect src(0, 0, w, h);
|
||||
if (w > sw || h > sh) {
|
||||
float aspect = w / h;
|
||||
if (w > sw) {
|
||||
w = sw;
|
||||
h = sw / aspect;
|
||||
}
|
||||
if (h > sh) {
|
||||
h = sh;
|
||||
w = sh * aspect;
|
||||
}
|
||||
}
|
||||
BRect dst(sw - w, sh - h, w, h);
|
||||
spacer->SetOverlayBitmap(cat, src, dst, NULL, B_FOLLOW_RIGHT|B_FOLLOW_BOTTOM);
|
||||
} else {
|
||||
spacer->ClearOverlayBitmap();
|
||||
}
|
||||
}
|
||||
HaikuLooperWindow::~HaikuLooperWindow() {
|
||||
delete ref_handler;
|
||||
delete pause_bitmap;
|
||||
|
@ -245,6 +287,13 @@ void HaikuLooperWindow::MessageReceived(BMessage *msg) {
|
|||
case CMD_PREFS: {
|
||||
prefs_subwindow->Show();
|
||||
} break;
|
||||
case CMD_SET_CAT: {
|
||||
BBitmap *cat = NULL;
|
||||
if (msg->FindBitmap("cat", &cat) != B_OK) {
|
||||
cat = NULL;
|
||||
}
|
||||
UpdateCat(cat);
|
||||
}
|
||||
case CMD_UPDATE_LABEL_SETTING: {
|
||||
volume_slider->MessageReceived(msg);
|
||||
speed_slider->MessageReceived(msg);
|
||||
|
|
|
@ -59,6 +59,8 @@ class HaikuLooperWindow : public BWindow {
|
|||
BButton *restart_btn;
|
||||
BButton *stop_btn;
|
||||
BButton *pause_resume_btn;
|
||||
BView *spacer;
|
||||
void UpdateCat(BBitmap *cat);
|
||||
bool volume_clicked = false;
|
||||
bool speed_clicked = false;
|
||||
bool pitch_clicked = false;
|
||||
|
|
|
@ -13,10 +13,25 @@ using namespace Looper::Options;
|
|||
#define CMD_SET_SETTING 0x1002
|
||||
#define CMD_REVERT 0x1003
|
||||
#define CMD_APPLY 0x1004
|
||||
#define CMD_SET_SETTING_CHECKBOX = 0x1005
|
||||
bool show_icons, show_labels;
|
||||
|
||||
HaikuPrefsWindow::HaikuPrefsWindow(BLooper *next_handler) : BWindow(BRect(100, 100, 0, 0), "Preferences", B_TITLED_WINDOW, B_AUTO_UPDATE_SIZE_LIMITS) {
|
||||
this->next_handler = next_handler;
|
||||
|
||||
for (auto &cat : get_cat_data()) {
|
||||
switch (cat.get_type()) {
|
||||
case CatDataType::Memory: {
|
||||
MemoryCat mem_cat = cat.get_memory_cat();
|
||||
|
||||
AddCat(cat.get_name(), LoadCat(mem_cat.get_ptr(), mem_cat.get_len(), cat.get_path().c_str()));
|
||||
} break;
|
||||
case CatDataType::File: {
|
||||
fs::path cat_path = cat.get_path();
|
||||
AddCat(cat.get_name(), LoadCat(cat_path.c_str()));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
auto *root_layout = new BGroupLayout(B_VERTICAL);
|
||||
SetLayout(root_layout);
|
||||
root_layout->SetSpacing(0.0);
|
||||
|
@ -56,7 +71,35 @@ HaikuPrefsWindow::HaikuPrefsWindow(BLooper *next_handler) : BWindow(BRect(100, 1
|
|||
label_settings_layout->AddView(labels_only);
|
||||
label_settings_layout->AddView(icons_only);
|
||||
label_settings_layout->AddView(both_labels_icons);
|
||||
box->AddChild(label_settings_group);
|
||||
BMessage *cat_enable_msg = new BMessage(CMD_SET_SETTING_CHECKBOX);
|
||||
cat_enable_msg->AddString("pref_path", "ui.cat_enable");
|
||||
cat_enable = new BCheckBox("Enable Cat", cat_enable_msg);
|
||||
BGroupView *cat_group = new BGroupView(B_VERTICAL);
|
||||
BGroupLayout *cat_layout = cat_group->GroupLayout();
|
||||
for (auto &kv : cats) {
|
||||
BMessage *msg = new BMessage(CMD_SET_SETTING);
|
||||
msg->AddString("pref_path", "ui.cat");
|
||||
msg->AddString("pref_value", kv.first.c_str());
|
||||
BRadioButton *cat_radio = new BRadioButton(fmt::format("prefs:cat:{}", kv.first).c_str(), msg);
|
||||
cat_btns[kv.first] = cat_radio;
|
||||
BBitmap *cat_bmp = kv.second;
|
||||
BRect src = cat_bmp->bounds();
|
||||
float w = src.Width(), h = src.Height();
|
||||
float rw = cat_radio->Bounds().Width(), rh = cat_radio->Bounds().Height();
|
||||
if (w > rw || h > rh) {
|
||||
float aspect = w / h;
|
||||
if (h > rh) {
|
||||
h = rh;
|
||||
w = h * aspect;
|
||||
}
|
||||
if (w > rw) {
|
||||
w = rw;
|
||||
h = h / aspect;
|
||||
}
|
||||
}
|
||||
BRect dst(rw - w, (rh - h) / 2, rw, rh);
|
||||
cat_radio->SetOverlayBitmap(cat_bmp, src, dst, B_FOLLOW_RIGHT|B_FOLLOW_TOP_BOTTOM);
|
||||
}
|
||||
root_layout->AddView(box, 3.0);
|
||||
BGroupView *btn_view = new BGroupView(B_HORIZONTAL);
|
||||
BGroupLayout *btn_box = btn_view->GroupLayout();
|
||||
|
@ -102,7 +145,12 @@ void HaikuPrefsWindow::MessageReceived(BMessage *msg) {
|
|||
else restart_warning->Hide();
|
||||
set_option<std::string>("ui.haiku.label_setting", new_label_setting);
|
||||
update_label_setting();
|
||||
set_option<std::string>("ui.cat", cat_id);
|
||||
set_option<bool>("ui.enable_cat", enable_cat);
|
||||
next_handler->PostMessage(CMD_UPDATE_LABEL_SETTING);
|
||||
BMessage *cat_msg = new BMessage(CMD_SET_CAT);
|
||||
if (enable_cat) cat_msg->AddBitmap("cat", cats[cat_id])
|
||||
next_handler->PostMessage(cat_msg);
|
||||
} break;
|
||||
case CMD_REVERT: {
|
||||
set_options_changed(false);
|
||||
|
@ -117,8 +165,16 @@ void HaikuPrefsWindow::MessageReceived(BMessage *msg) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
cat_btns[cat_id].SetValue(B_CONTROL_OFF);
|
||||
cat_id = get_option<std::string>("ui.cat", cats.empty() ? "" : cats.begin()->first);
|
||||
if (cat_btns.contains(cat_id)) cat_btns[cat_id].SetValue(B_CONTROL_ON);
|
||||
enable_cat = get_option<bool>("ui.enable_cat", false);
|
||||
cat_enable->SetValue(enable_cat ? B_CONTROL_ON : B_CONTROL_OFF);
|
||||
update_label_setting();
|
||||
next_handler->PostMessage(CMD_UPDATE_LABEL_SETTING);
|
||||
BMessage *cat_msg = new BMessage(CMD_SET_CAT);
|
||||
if (enable_cat) cat_msg->AddBitmap("cat", cats[cat_id])
|
||||
next_handler->PostMessage(cat_msg);
|
||||
} break;
|
||||
case CMD_FRONTEND: {
|
||||
auto option = msg->GetInt32("be:value", cur_option);
|
||||
|
@ -136,6 +192,21 @@ void HaikuPrefsWindow::MessageReceived(BMessage *msg) {
|
|||
if (std::string(setting) == "ui.haiku.label_setting") {
|
||||
new_label_setting = setting_value;
|
||||
set_options_changed(true);
|
||||
} else if (std::string(setting) == "ui.cat") {
|
||||
cat_id = setting_value;
|
||||
set_options_changed(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case CMD_SET_SETTING_CHECKBOX: {
|
||||
const char *setting;
|
||||
if (msg->FindString("pref_path", &setting) == B_OK) {
|
||||
int32 setting_value_int32;
|
||||
if (msg->FindInt32("be:value", &setting_value_int32) == B_OK) {
|
||||
if (std::string(setting) == "ui.enable_cat") {
|
||||
enable_cat = setting_value_int32 == B_CONTROL_ON;
|
||||
set_options_changed(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,17 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#define CMD_SET_CAT 0x1010
|
||||
class HaikuPrefsWindow : public BWindow {
|
||||
std::vector<std::string> backend_ids;
|
||||
std::string cat_id;
|
||||
bool enable_cat;
|
||||
std::map<std::string, BBitmap*> cats;
|
||||
std::map<std::string, BRadioButton*> cat_btns;
|
||||
BCheckBox *cat_enable;
|
||||
void AddCat(std:string name, BBitmap* img);
|
||||
BBitmap *LoadCat(const char *path);
|
||||
BBitmap *LoadCat(const void *ptr, size_t len, const char *name);
|
||||
int32 cur_option = 0;
|
||||
BLooper *next_handler;
|
||||
BStringView *restart_warning;
|
||||
|
|
|
@ -329,8 +329,7 @@ void RendererBackend::BackendInit() {
|
|||
}
|
||||
#endif
|
||||
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
|
||||
const vector<unsigned char> icon_data = DecodeBase85(icon_compressed_data_base85);
|
||||
SDL_Surface* icon = IMG_Load_RW(SDL_RWFromConstMem(icon_data.data(), icon_data.size()), 1);
|
||||
SDL_Surface* icon = IMG_Load_RW(SDL_RWFromConstMem(icon_data, icon_size), 1);
|
||||
SDL_SetWindowIcon(window, icon);
|
||||
|
||||
// Setup Dear ImGui context
|
||||
|
@ -403,7 +402,8 @@ int RendererBackend::Run() {
|
|||
#else
|
||||
try {
|
||||
BackendInit();
|
||||
} catch (std::exception) {
|
||||
} catch (std::exception &e) {
|
||||
ERROR.writefln("Error occurred during initialization: %s", e.what());
|
||||
return -1;
|
||||
}
|
||||
started = true;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "thirdparty/CLI11.hpp"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
#include <web_functions.hpp>
|
||||
#include <cats.hpp>
|
||||
using namespace Looper::Options;
|
||||
void MainLoop::Init() {
|
||||
#ifdef PORTALS
|
||||
|
@ -28,6 +29,25 @@ void MainLoop::Init() {
|
|||
theme_editor = false;
|
||||
stopped = true;
|
||||
about_window = false;
|
||||
for (auto &cat : get_cat_data()) {
|
||||
switch (cat.get_type()) {
|
||||
case CatDataType::Memory: {
|
||||
auto mem_cat = cat.get_memory_cat();
|
||||
AddCat(cat.get_name(), LoadCatFromMemory(mem_cat.get_ptr(), mem_cat.get_len(), cat.get_path().c_str()));
|
||||
} break;
|
||||
case CatDataType::File: {
|
||||
fs::path cat_path = cat.get_path();
|
||||
try {
|
||||
AddCat(cat.get_name(), LoadCat(cat_path.string()));
|
||||
} catch (std::exception &e) {
|
||||
WARNING.writefln("Failed to load cat %s at path %s: %s", cat_path.c_str(), cat.get_name().c_str(), e.what());
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
WARNING.writefln("Invalid cat type with numerical value: %d", cat.get_type());
|
||||
} break;
|
||||
}
|
||||
}
|
||||
string lang;
|
||||
{
|
||||
Json::Value config;
|
||||
|
@ -103,6 +123,11 @@ void MainLoop::Init() {
|
|||
accent_color.w = (float)get_option<double>("ui.imgui.accent_color.a", accent_color.w);
|
||||
debug_mode = get_option<bool>("ui.imgui.debug_mode", false);
|
||||
}
|
||||
{
|
||||
enable_cat = get_option<bool>("ui.enable_cat", false);
|
||||
cat_setting = get_option<std::string>("ui.cat", cats.empty() ? "" : cats.begin()->first);
|
||||
if (cats.contains(cat_setting)) cat = cats[cat_setting];
|
||||
}
|
||||
Theme::updateAvailableThemes();
|
||||
if (Theme::availableThemes.empty()) {
|
||||
path lightPath = Theme::themeDir / "light.toml";
|
||||
|
@ -134,6 +159,44 @@ void MainLoop::Init() {
|
|||
void MainLoop::Drop(std::string file) {
|
||||
LoadFile(file);
|
||||
}
|
||||
SDL_Texture *MainLoop::LoadCat(File *file) {
|
||||
size_t pos = file->get_pos();
|
||||
file->seek(0, SeekType::SET);
|
||||
std::string fname = file->name;
|
||||
|
||||
SDL_RWops *rwops = get_sdl_file(file);
|
||||
const char *ext = std::filesystem::path(fname).extension().c_str();
|
||||
DEBUG.writefln("Extension: %s\n", ext);
|
||||
if (ext[0] == '.') ext = ext + 1;
|
||||
SDL_Texture *tex = IMG_LoadTextureTyped_RW(rend, rwops, 0, ext);
|
||||
delete rwops;
|
||||
if (!tex) throw CustomException(fmt::format("Failed to load cat {}: {}", fname, IMG_GetError()));
|
||||
file->seek(pos, SeekType::SET);
|
||||
return tex;
|
||||
}
|
||||
SDL_Texture *MainLoop::LoadCat(std::string path) {
|
||||
|
||||
std::string fname = path;
|
||||
|
||||
SDL_RWops *rwops = SDL_RWFromFile(path.c_str(), "rb");
|
||||
SDL_Texture *tex = IMG_LoadTexture_RW(rend, rwops, 1);
|
||||
if (!tex) throw CustomException(fmt::format("Failed to load cat {}: {}", fname, IMG_GetError()));
|
||||
return tex;
|
||||
}
|
||||
SDL_Texture *MainLoop::LoadCatFromMemory(const void *ptr, size_t len, const char *name) {
|
||||
std::string fname = name;
|
||||
|
||||
SDL_RWops *rwops = SDL_RWFromConstMem(ptr, len);
|
||||
SDL_Texture *tex = IMG_LoadTexture_RW(rend, rwops, 1);
|
||||
if (!tex) throw CustomException(fmt::format("Failed to load cat {}: {}", fname, IMG_GetError()));
|
||||
return tex;
|
||||
}
|
||||
void MainLoop::AddCat(std::string name, SDL_Texture *tex) {
|
||||
if (cats.contains(name)) {
|
||||
SDL_DestroyTexture(cats[name]);
|
||||
}
|
||||
cats[name] = tex;
|
||||
}
|
||||
void MainLoop::FileLoaded() {
|
||||
auto file_maybe = playback->get_current_title();
|
||||
if (file_maybe.has_value()) {
|
||||
|
@ -216,6 +279,17 @@ void MainLoop::GuiFunction() {
|
|||
ImGui::SetNextWindowDockID(dockid);
|
||||
ImGui::Begin(_TRI_CTX(ICON_FK_PLAY, "Main window title", "Player"), nullptr, 0);
|
||||
{
|
||||
ImGui::SetCursorPosY(0);
|
||||
float y_pos = ImGui::GetWindowHeight() - ImGui::GetFrameHeightWithSpacing() - ImGui::GetFrameHeight() - ImGui::GetStyle().WindowPadding.y;
|
||||
if (enable_cat && cat) {
|
||||
int cw, ch;
|
||||
SDL_QueryTexture(cat, NULL, NULL, &cw, &ch);
|
||||
float aspect = ((float)cw) / ((float)ch);
|
||||
float x_size = y_pos * aspect;
|
||||
float x_pos = ImGui::GetWindowWidth() - ImGui::GetStyle().WindowPadding.x - x_size;
|
||||
ImGui::SetCursorPosX(x_pos);
|
||||
ImGui::Image((ImTextureID)cat, ImVec2(x_size, y_pos));
|
||||
}
|
||||
float centerSpace = ImGui::GetWindowHeight() - ImGui::GetFrameHeightWithSpacing() - ImGui::GetFrameHeight() - ImGui::GetStyle().WindowPadding.y;
|
||||
if (streams.size() > 0) {
|
||||
static string filter = "";
|
||||
|
@ -255,7 +329,7 @@ void MainLoop::GuiFunction() {
|
|||
}
|
||||
ImGui::EndChildFrame();
|
||||
}
|
||||
ImGui::SetCursorPosY(ImGui::GetWindowHeight() - ImGui::GetFrameHeightWithSpacing() - ImGui::GetFrameHeight() - ImGui::GetStyle().WindowPadding.y);
|
||||
ImGui::SetCursorPosY(y_pos);
|
||||
if (ImGui::Button(playback->IsPaused() ? ICON_FK_PLAY "##Pause" : ICON_FK_PAUSE "##Pause")) {
|
||||
playback->Pause();
|
||||
}
|
||||
|
@ -517,6 +591,54 @@ void MainLoop::GuiFunction() {
|
|||
set_option<std::string>("ui.imgui.lang", lang);
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_Texture *disp_cat = nullptr;
|
||||
if (ImGui::Checkbox(_TR_CTX("Preference | cat enable checkbox", "Enable cat"), &enable_cat)) {
|
||||
disp_cat = nullptr;
|
||||
set_option<bool>("ui.enable_cat", enable_cat);
|
||||
}
|
||||
if (enable_cat) {
|
||||
|
||||
ImVec2 TableSize = ImVec2(0, 0);
|
||||
if (ImGui::BeginTable("##Cats", 2, ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoSavedSettings|ImGuiTableFlags_ScrollY, TableSize)) {
|
||||
ImGui::TableSetupColumn("Cat Name", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Preview", 0);
|
||||
for (auto &kv : cats) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
if (ImGui::Selectable(kv.first.c_str(), kv.first == cat_setting, 0)) {
|
||||
cat_setting = kv.first;
|
||||
cat = cats[cat_setting];
|
||||
set_option<std::string>("ui.cat", cat_setting);
|
||||
}
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
int cw, ch;
|
||||
SDL_QueryTexture(kv.second, NULL, NULL, &cw, &ch);
|
||||
float aspect = ((float)cw) / ((float)ch);
|
||||
bool portrait = ch > cw;
|
||||
ImVec2 size = ImVec2(16.0f * aspect, 16.0f);
|
||||
if (ImGui::ImageButton(fmt::format("disp_cat_{}", kv.first).c_str(), (ImTextureID)kv.second, size)) {
|
||||
if (disp_cat == kv.second) {
|
||||
disp_cat = nullptr;
|
||||
} else {
|
||||
disp_cat = kv.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
bool show_cat_preview = disp_cat != nullptr;
|
||||
if (show_cat_preview && ImGui::Begin("Cat Preview", &show_cat_preview, ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
int cw, ch;
|
||||
SDL_QueryTexture(disp_cat, NULL, NULL, &cw, &ch);
|
||||
ImGui::Image((ImTextureID)disp_cat, ImVec2(cw, ch));
|
||||
ImGui::End();
|
||||
}
|
||||
// Handle window close.
|
||||
if (!show_cat_preview) {
|
||||
disp_cat = nullptr;
|
||||
}
|
||||
}
|
||||
if (override_lang) {
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - (ImGui::GetFontSize()) - ((ImGui::GetStyle().ItemSpacing.x + (ImGui::GetStyle().FramePadding.x * 2.0f))) - (ImGui::GetStyle().WindowPadding.x));
|
||||
|
@ -689,13 +811,18 @@ void MainLoop::LoadFile(std::string file) {
|
|||
playback->Start(file);
|
||||
}
|
||||
void MainLoop::Deinit() {
|
||||
|
||||
for (auto kv : cats) {
|
||||
SDL_DestroyTexture(kv.second);
|
||||
}
|
||||
cats.clear();
|
||||
{
|
||||
path themePath(theme->file_path);
|
||||
themePath = themePath.stem();
|
||||
if (!themePath.empty()) {
|
||||
set_option<std::string>("ui.imgui.theme", themePath.string());
|
||||
}
|
||||
set_option<bool>("ui.enable_cat", enable_cat);
|
||||
set_option<std::string>("ui.cat", cat_setting);
|
||||
set_option<double>("ui.imgui.accent_color.h", accent_color.x);
|
||||
set_option<double>("ui.imgui.accent_color.s", accent_color.y);
|
||||
set_option<double>("ui.imgui.accent_color.v", accent_color.z);
|
||||
|
@ -731,7 +858,7 @@ void ImGuiUIBackend::QuitHandler() {
|
|||
// Main code
|
||||
int ImGuiUIBackend::run(std::vector<std::string> realArgs, int argc, char** argv)
|
||||
{
|
||||
SDL_setenv("SDL_VIDEO_X11_WMCLASS", "looper");
|
||||
SDL_setenv("SDL_VIDEO_X11_WMCLASS", "looper", 1);
|
||||
int possible_error = UIBackend::run(realArgs, argc, argv);
|
||||
if (possible_error != 0) {
|
||||
return possible_error;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
using namespace std::filesystem;
|
||||
using std::string;
|
||||
#define IMGUI_FRONTEND
|
||||
|
||||
class MainLoop : public RendererBackend {
|
||||
bool show_demo_window = false;
|
||||
FileBrowser fileDialog = FileBrowser(false);
|
||||
|
@ -49,6 +50,8 @@ class MainLoop : public RendererBackend {
|
|||
bool property_editor = false;
|
||||
bool restart_needed = false;
|
||||
bool stopped = true;
|
||||
bool enable_cat = false;
|
||||
std::string cat_setting = "__default__";
|
||||
std::vector<UIBackend*> backends;
|
||||
UIBackend *cur_backend;
|
||||
friend class ImGuiUIBackend;
|
||||
|
@ -59,6 +62,12 @@ class MainLoop : public RendererBackend {
|
|||
std::map<std::string, std::string> string_properties;
|
||||
std::vector<Property> properties;
|
||||
std::vector<PlaybackStream> streams;
|
||||
std::map<std::string, SDL_Texture*> cats;
|
||||
SDL_Texture *cat = nullptr;
|
||||
SDL_Texture *LoadCatFromMemory(const void *ptr, size_t len, const char *name);
|
||||
SDL_Texture *LoadCat(File *file);
|
||||
SDL_Texture *LoadCat(std::string path);
|
||||
void AddCat(std::string name, SDL_Texture *tex);
|
||||
public:
|
||||
Playback *playback;
|
||||
vector<std::string> args;
|
||||
|
|
|
@ -80,9 +80,11 @@ LooperWindow::LooperWindow(Playback *playback) : QMainWindow() {
|
|||
help_menu->addAction(about_item);
|
||||
bar->addMenu(file_menu);
|
||||
bar->addMenu(help_menu);
|
||||
root_layout->addWidget(bar);
|
||||
QSpacerItem *spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
|
||||
root_layout->addSpacerItem(spacer);
|
||||
cat_disp = new QLabel();
|
||||
cat_disp->setAlignment(Qt::Alignment::enum_type::AlignRight);
|
||||
root_layout->addWidget(cat_disp);
|
||||
QBoxLayout *top_row = new QBoxLayout(QBoxLayout::LeftToRight, this);
|
||||
pause_resume_btn = new QPushButton("Pause", this);
|
||||
QObject::connect(pause_resume_btn, &QPushButton::pressed, [=,this]() {
|
||||
|
@ -146,6 +148,8 @@ LooperWindow::LooperWindow(Playback *playback) : QMainWindow() {
|
|||
timer->setInterval(1);
|
||||
timer->start();
|
||||
QObject::connect(prefs_window, &PrefsWindow::settings_changed, this, &LooperWindow::update_label_setting);
|
||||
QObject::connect(prefs_window, &PrefsWindow::cat_set, this, &LooperWindow::update_cat);
|
||||
QObject::connect(prefs_window, &PrefsWindow::cat_unset, this, &LooperWindow::clear_cat);
|
||||
//setLayout(layout);
|
||||
}
|
||||
void LooperWindow::update_label_setting(bool labels_visible, bool icons_visible) {
|
||||
|
@ -173,3 +177,11 @@ void LooperWindow::update_label_setting(bool labels_visible, bool icons_visible)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LooperWindow::clear_cat() {
|
||||
cat_disp->hide();
|
||||
}
|
||||
void LooperWindow::update_cat(QPixmap &img) {
|
||||
cat_disp->setPixmap(img);
|
||||
cat_disp->show();
|
||||
}
|
||||
|
|
|
@ -41,7 +41,10 @@ class LooperWindow : public QMainWindow {
|
|||
QAction *about_item;
|
||||
QFileDialog *file_dialog;
|
||||
QBoxLayout *root_layout;
|
||||
QLabel *cat_disp;
|
||||
void update_label_setting(bool labels_visible, bool icons_visible);
|
||||
void update_cat(QPixmap &img);
|
||||
void clear_cat();
|
||||
public:
|
||||
AboutWindow *about_window;
|
||||
PrefsWindow *prefs_window;
|
||||
|
|
|
@ -1,10 +1,35 @@
|
|||
#include "preferences.h"
|
||||
#include <QMenu>
|
||||
#include <QBoxLayout>
|
||||
#include <QBuffer>
|
||||
#include <QImage>
|
||||
#include <QDataStream>
|
||||
#include <backend.hpp>
|
||||
#include <options.hpp>
|
||||
#include <cats.hpp>
|
||||
#include <log.hpp>
|
||||
using namespace Looper::Options;
|
||||
PrefsWindow::PrefsWindow() {
|
||||
for (auto &cat : get_cat_data()) {
|
||||
switch (cat.get_type()) {
|
||||
case CatDataType::Memory: {
|
||||
QImage image;
|
||||
QBuffer buffer;
|
||||
QDataStream in(&buffer);
|
||||
auto mem_cat = cat.get_memory_cat();
|
||||
buffer.setData(const_cast<char*>((const char*)mem_cat.get_ptr()), mem_cat.get_len());
|
||||
in >> image;
|
||||
cats[cat.get_name()] = QPixmap::fromImage(image);
|
||||
} break;
|
||||
case CatDataType::File: {
|
||||
try {
|
||||
cats[cat.get_name()] = QPixmap(cat.get_path().c_str());
|
||||
} catch (std::exception e) {
|
||||
WARNING.writefln("Failed to load cat %s at path %s: %s", cat.get_name().c_str(), cat.get_path().c_str(), e.what());
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
auto *root_layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
|
||||
this->setLayout(root_layout);
|
||||
restart_warning = new QLabel("A restart is needed to apply some changes.", this);
|
||||
|
@ -49,6 +74,31 @@ PrefsWindow::PrefsWindow() {
|
|||
label_settings_group->addWidget(icons_only);
|
||||
label_settings_group->addWidget(both_labels_icons);
|
||||
root_layout->addWidget(frame);
|
||||
cat_enable = new QCheckBox("Enable Cat", this);
|
||||
QObject::connect(cat_enable, &QCheckBox::pressed, [=,this]() {
|
||||
this->enable_cat = cat_enable->isChecked();
|
||||
this->set_options_changed(true);
|
||||
});
|
||||
root_layout->addWidget(cat_enable);
|
||||
QFrame *catFrame = new QFrame(this);
|
||||
catFrame->setWindowTitle("Cat Selection");
|
||||
auto *cat_btns_layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
|
||||
catFrame->setLayout(cat_btns_layout);
|
||||
for (auto &kv : cats) {
|
||||
auto id = kv.first;
|
||||
auto pixmap = kv.second;
|
||||
QRadioButton *cat_radio = new QRadioButton(id.c_str(), catFrame);
|
||||
QLabel *cat_img = new QLabel(cat_radio);
|
||||
cat_img->setPixmap(pixmap);
|
||||
cat_img->setAlignment(Qt::Alignment::enum_type::AlignRight);
|
||||
QObject::connect(cat_radio, &QRadioButton::pressed, [=,this]() {
|
||||
this->cat_setting = id;
|
||||
this->set_options_changed(true);
|
||||
});
|
||||
cat_btns_layout->addWidget(cat_radio);
|
||||
cat_btns[id] = cat_radio;
|
||||
}
|
||||
root_layout->addWidget(catFrame);
|
||||
QWidget *btn_view = new QWidget(this);
|
||||
QBoxLayout *btn_box = new QBoxLayout(QBoxLayout::LeftToRight, this);
|
||||
revert_btn = new QPushButton("Revert", btn_view);
|
||||
|
@ -83,6 +133,21 @@ void PrefsWindow::revert() {
|
|||
if (new_frontend != "qt") restart_warning->show();
|
||||
else restart_warning->hide();
|
||||
frontend_btn->setText(new_frontend.c_str());
|
||||
enable_cat = get_option<bool>("ui.enable_cat");
|
||||
cat_enable->setCheckState(enable_cat ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
||||
cat_setting = get_option<std::string>("ui.cat", cats.empty() ? "" : cats.begin()->first);
|
||||
std::map<QRadioButton*, bool> radio_btn_values = {{labels_only, false}, {icons_only, false}, {both_labels_icons, false}};
|
||||
if (new_label_setting == "labels") {
|
||||
radio_btn_values[labels_only] = true;
|
||||
} else if (new_label_setting == "icons") {
|
||||
radio_btn_values[icons_only] = true;
|
||||
} else if (new_label_setting == "both") {
|
||||
radio_btn_values[both_labels_icons] = true;
|
||||
}
|
||||
for (auto &kv : radio_btn_values) {
|
||||
kv.first->setDown(kv.second);
|
||||
}
|
||||
if (cat_btns.contains(cat_setting)) cat_btns[cat_setting]->setDown(true);
|
||||
update_label_setting();
|
||||
}
|
||||
void PrefsWindow::apply() {
|
||||
|
@ -93,4 +158,8 @@ void PrefsWindow::apply() {
|
|||
else restart_warning->hide();
|
||||
frontend_btn->setText(new_frontend.c_str());
|
||||
update_label_setting();
|
||||
set_option<bool>("ui.enable_cat", enable_cat);
|
||||
set_option<std::string>("ui.cat", cat_setting);
|
||||
if (enable_cat && cats.contains(cat_setting)) emit(cat_set(cats[cat_setting]));
|
||||
else emit(cat_unset());
|
||||
}
|
||||
|
|
|
@ -18,12 +18,17 @@ class PrefsWindow : public QWidget {
|
|||
QPushButton *frontend_btn;
|
||||
QMenu *frontend_menu;
|
||||
std::vector<QAction*> frontend_options;
|
||||
std::map<std::string, QPixmap> cats;
|
||||
std::map<std::string, QRadioButton*> cat_btns;
|
||||
QCheckBox *menu_icons;
|
||||
QRadioButton *labels_only;
|
||||
QRadioButton *icons_only;
|
||||
QRadioButton *both_labels_icons;
|
||||
QPushButton *revert_btn;
|
||||
QPushButton *apply_btn;
|
||||
QCheckBox *cat_enable;
|
||||
bool enable_cat;
|
||||
std::string cat_setting;
|
||||
void update_label_setting();
|
||||
void set_options_changed(bool changed);
|
||||
void revert();
|
||||
|
@ -31,5 +36,7 @@ class PrefsWindow : public QWidget {
|
|||
public:
|
||||
PrefsWindow();
|
||||
Q_SIGNALS:
|
||||
void cat_set(QPixmap &img);
|
||||
void cat_unset();
|
||||
void settings_changed(bool use_labels, bool use_icons);
|
||||
};
|
11
base85.cpp
11
base85.cpp
|
@ -4,10 +4,11 @@ using std::vector;
|
|||
static unsigned int DecodeBase85Byte(char c) {
|
||||
return c >= '\\' ? c-36 : c-35;
|
||||
}
|
||||
vector<unsigned char> DecodeBase85(const char *src) {
|
||||
vector<unsigned char> dst_vec;
|
||||
dst_vec.resize(((strlen(src) + 4) / 5) * 4);
|
||||
unsigned char *dst = dst_vec.data();
|
||||
vector<unsigned char> *DecodeBase85(const char *src) {
|
||||
vector<unsigned char> *dst_vec = new vector<unsigned char>();
|
||||
dst_vec->resize(((strlen(src) + 4) / 5) * 4);
|
||||
unsigned char *dst = dst_vec->data();
|
||||
memset(dst, 0, dst_vec->size());
|
||||
size_t dst_size = 0;
|
||||
while (*src)
|
||||
{
|
||||
|
@ -17,6 +18,6 @@ vector<unsigned char> DecodeBase85(const char *src) {
|
|||
dst += 4;
|
||||
dst_size += 4;
|
||||
}
|
||||
dst_vec.resize(dst_size);
|
||||
dst_vec->resize(dst_size);
|
||||
return dst_vec;
|
||||
}
|
6
base85.h
6
base85.h
|
@ -3,8 +3,4 @@
|
|||
#include <cstring>
|
||||
#include <string>
|
||||
// Modified from Dear ImGui
|
||||
std::vector<unsigned char> DecodeBase85(const char *src);
|
||||
// May not be needed now, but could be useful in the future.
|
||||
static inline std::vector<unsigned char> DecodeBase85(const std::string src) {
|
||||
return DecodeBase85(src.c_str());
|
||||
}
|
||||
std::vector<unsigned char> *DecodeBase85(const char *src);
|
88
cats.hpp
Normal file
88
cats.hpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
namespace fs = std::filesystem;
|
||||
class MemoryCat {
|
||||
const void *ptr;
|
||||
size_t len;
|
||||
bool owned;
|
||||
public:
|
||||
inline size_t get_len() {
|
||||
return len;
|
||||
}
|
||||
inline const void *get_ptr() {
|
||||
return (const void*)ptr;
|
||||
}
|
||||
inline ~MemoryCat() {
|
||||
if (owned) free((void*)ptr);
|
||||
}
|
||||
/// \brief Constructor that doesn't take ownership of the pointer
|
||||
inline MemoryCat(const void *ptr, size_t len) {
|
||||
this->owned = false;
|
||||
this->ptr = ptr;
|
||||
this->len = len;
|
||||
}
|
||||
/// \brief Constructor that takes ownership of the pointer
|
||||
inline MemoryCat(void *ptr, size_t len) {
|
||||
this->owned = true;
|
||||
this->ptr = ptr;
|
||||
this->len = len;
|
||||
}
|
||||
};
|
||||
enum class CatDataType {
|
||||
Memory,
|
||||
File
|
||||
};
|
||||
class CatData {
|
||||
std::optional<MemoryCat> mem;
|
||||
fs::path path;
|
||||
std::string name;
|
||||
CatDataType type;
|
||||
public:
|
||||
inline std::string get_name() {
|
||||
return name;
|
||||
}
|
||||
inline CatDataType get_type() {
|
||||
return type;
|
||||
}
|
||||
inline fs::path get_path() {
|
||||
return path;
|
||||
}
|
||||
inline MemoryCat get_memory_cat() {
|
||||
if (type == CatDataType::Memory) {
|
||||
return mem.value();
|
||||
} else {
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
inline MemoryCat as_memory_cat() {
|
||||
if (mem.has_value()) return mem.value();
|
||||
if (type == CatDataType::File) {
|
||||
FILE *file = fopen(path.c_str(), "rb");
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t len = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
void *ptr = malloc(len);
|
||||
len = fread(ptr, 1, len, file);
|
||||
ptr = realloc(ptr, len);
|
||||
MemoryCat output(ptr, len);
|
||||
this->mem = output;
|
||||
return output;
|
||||
}
|
||||
throw std::exception();
|
||||
}
|
||||
inline CatData(const void *ptr, size_t len, std::string name, fs::path virtual_path) {
|
||||
this->type = CatDataType::Memory;
|
||||
this->name = name;
|
||||
this->path = virtual_path;
|
||||
this->mem = MemoryCat(ptr, len);
|
||||
}
|
||||
inline CatData(fs::path path) {
|
||||
this->name = path.stem();
|
||||
this->type = CatDataType::File;
|
||||
this->path = path;
|
||||
}
|
||||
};
|
||||
extern std::vector<CatData> &get_cat_data();
|
|
@ -1,6 +1,7 @@
|
|||
#include "file_backend.hpp"
|
||||
#include <filesystem>
|
||||
#include <string.h>
|
||||
#include <cassert>
|
||||
|
||||
File::File() {
|
||||
}
|
||||
|
@ -56,9 +57,18 @@ bool CFile::is_open() {
|
|||
return file != NULL;
|
||||
}
|
||||
MemFile::MemFile() {
|
||||
memory_owned = false;
|
||||
ptr = NULL;
|
||||
len = 0;
|
||||
}
|
||||
void MemFile::open_memory(void *ptr, size_t len, const char *name) {
|
||||
assert(name != NULL);
|
||||
this->name = strdup(name);
|
||||
this->ptr = ptr;
|
||||
this->len = len;
|
||||
this->pos = 0;
|
||||
memory_owned = false;
|
||||
}
|
||||
void MemFile::open(const char *path) {
|
||||
CFile file;
|
||||
file.open(path);
|
||||
|
@ -67,9 +77,10 @@ void MemFile::open(const char *path) {
|
|||
this->len = file.read(this->ptr, 1, file.get_len());
|
||||
file.close();
|
||||
this->pos = 0;
|
||||
memory_owned = true;
|
||||
}
|
||||
void MemFile::close() {
|
||||
free(this->ptr);
|
||||
if (memory_owned) free(this->ptr);
|
||||
free((void*)this->name);
|
||||
this->ptr = NULL;
|
||||
this->name = NULL;
|
||||
|
|
|
@ -93,8 +93,10 @@ class MemFile : public File {
|
|||
void *ptr;
|
||||
size_t len;
|
||||
int64_t pos;
|
||||
bool memory_owned = true;
|
||||
public:
|
||||
MemFile();
|
||||
void open_memory(void *ptr, size_t len, const char *name = "<mem_file>");
|
||||
void open(const char *path) override;
|
||||
void close() override;
|
||||
size_t read(void *ptr, size_t size, size_t len) override;
|
||||
|
|
20
main.cpp
20
main.cpp
|
@ -16,6 +16,7 @@
|
|||
#include <image.h>
|
||||
#endif
|
||||
#include "web_functions.hpp"
|
||||
#include "cats.hpp"
|
||||
using namespace Looper;
|
||||
using namespace Looper::Options;
|
||||
using namespace Looper::Log;
|
||||
|
@ -24,6 +25,10 @@ extern "C" {
|
|||
void quit();
|
||||
}
|
||||
#endif
|
||||
std::vector<CatData> &get_cat_data() {
|
||||
static std::vector<CatData> data;
|
||||
return data;
|
||||
}
|
||||
std::unordered_set<LicenseData> license_data;
|
||||
std::unordered_set<LicenseData> &get_license_data() {
|
||||
return license_data;
|
||||
|
@ -175,6 +180,21 @@ extern "C" int looper_run_as_executable(std::vector<std::string> args) {
|
|||
}
|
||||
DEBUG.writeln("Loading options file...");
|
||||
load_options();
|
||||
{
|
||||
auto &cat_data = get_cat_data();
|
||||
cat_data.push_back(CatData(catoc_data, catoc_size, "Cat OC (Built-In)", "catoc.png"));
|
||||
#ifndef __EMSCRIPTEN__
|
||||
fs::path baseDir = get_prefs_path() / fs::path("looper") / fs::path("themes");
|
||||
if (!fs::exists(baseDir)) {
|
||||
fs::create_directories(baseDir);
|
||||
}
|
||||
for (auto const&dir_entry : std::filesystem::directory_iterator(baseDir)) {
|
||||
if (dir_entry.is_regular_file()) {
|
||||
cat_data.push_back(CatData(dir_entry.path()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
std::string backend_id = get_option<std::string>("ui.frontend", "imgui");
|
||||
UIBackend *backend = UIBackend::get_backend(ui_backend_option).value_or(UIBackend::get_backend(backend_id).value_or(UIBackend::get_first_backend()));
|
||||
int output = 0;
|
||||
|
|
Loading…
Reference in a new issue