Compare commits

...

10 commits

29 changed files with 12716 additions and 1218 deletions

3
.gitmodules vendored
View file

@ -10,3 +10,6 @@
[submodule "subprojects/jsoncpp"] [submodule "subprojects/jsoncpp"]
path = subprojects/jsoncpp path = subprojects/jsoncpp
url = https://github.com/open-source-parsers/jsoncpp url = https://github.com/open-source-parsers/jsoncpp
[submodule "subprojects/vgmstream"]
path = subprojects/vgmstream
url = https://github.com/vgmstream/vgmstream.git

8
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/neko-player.iml" filepath="$PROJECT_DIR$/.idea/neko-player.iml" />
</modules>
</component>
</project>

8
.idea/neko-player.iml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

10
.idea/vcs.xml Normal file
View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/IconFontCppHeaders" vcs="Git" />
<mapping directory="$PROJECT_DIR$/imgui" vcs="Git" />
<mapping directory="$PROJECT_DIR$/subprojects/SDL-Mixer-X" vcs="Git" />
<mapping directory="$PROJECT_DIR$/subprojects/jsoncpp" vcs="Git" />
</component>
</project>

325
RendererBackend.cpp Normal file
View file

@ -0,0 +1,325 @@
#include "RendererBackend.h"
#include "assets.h"
#include <vector>
#include "IconsForkAwesome.h"
#include "config.h"
#include <SDL_image.h>
#include <string>
#include "theme.h"
#include "imgui_stdlib.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_opengl3.h"
#include "base85.h"
#include <thread>
#include "translation.h"
using std::vector;
struct FontData {
const char* data;
const ImWchar *ranges;
};
ImFont *add_font(vector<FontData> data_vec, int size = 13) {
ImFont* font = nullptr;
ImGuiIO& io = ImGui::GetIO();
for (auto data : data_vec) {
ImFontConfig font_cfg = ImFontConfig();
font_cfg.SizePixels = size;
font_cfg.OversampleH = font_cfg.OversampleV = 1;
font_cfg.PixelSnapH = true;
if (font_cfg.SizePixels <= 0.0f)
font_cfg.SizePixels = 13.0f * 1.0f;
if (font != nullptr) {
font_cfg.DstFont = font;
font_cfg.MergeMode = true;
}
//font_cfg.EllipsisChar = (ImWchar)0x0085;
//font_cfg.GlyphOffset.y = 1.0f * IM_FLOOR(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units
const char* ttf_compressed_base85 = data.data;
const ImWchar* glyph_ranges = data.ranges;
auto new_font = io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, glyph_ranges);
if (font == nullptr) font = new_font;
}
{
ImFontConfig config;
config.MergeMode = true;
config.GlyphMinAdvanceX = size;
config.SizePixels = size;
config.DstFont = font;
static const ImWchar icon_ranges[] = { ICON_MIN_FK, ICON_MAX_FK, 0 };
io.Fonts->AddFontFromMemoryCompressedBase85TTF(forkawesome_compressed_data_base85, float(size), &config, icon_ranges);
}
return font;
}
RendererBackend::RendererBackend() {
}
RendererBackend::~RendererBackend() {
}
void RendererBackend::SetWindowTitle(const char *title) {
SDL_SetWindowTitle(window, title);
}
void RendererBackend::GuiFunction() {
// Do nothing by default.
}
void RendererBackend::UpdateScale() {
double prevScale = scale;
const double defaultDPI =
#ifdef __APPLE__
72.0;
#else
96.0;
#endif
float dpi = defaultDPI;
if (SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(window), NULL, &dpi, NULL) == 0){
scale = dpi / defaultDPI;
} else {
printf("WARNING: DPI couldn't be determined!\n");
scale = 1.0;
}
SDL_SetWindowSize(window, window_width * scale, window_height * scale);
AddFonts();
}
void RendererBackend::SetWindowSize(int w, int h) {
window_width = w;
window_height = h;
SDL_SetWindowSize(window, w * scale, h * scale);
}
void RendererBackend::GetWindowsize(int *w, int *h) {
int ww, wh;
SDL_GetWindowSize(window, &ww, &wh);
ww /= scale;
wh /= scale;
if (w) *w = ww;
if (h) *h = wh;
}
void RendererBackend::AddFonts() {
ImGui_ImplOpenGL3_DestroyFontsTexture();
auto& io = ImGui::GetIO(); (void)io;
io.Fonts->Clear();
add_font({FontData {notosans_regular_compressed_data_base85, io.Fonts->GetGlyphRangesDefault()}, FontData {notosansjp_regular_compressed_data_base85, io.Fonts->GetGlyphRangesJapanese()}}, 13 * scale);
title = add_font({FontData {notosans_thin_compressed_data_base85, io.Fonts->GetGlyphRangesDefault()}, FontData {notosansjp_thin_compressed_data_base85, io.Fonts->GetGlyphRangesJapanese()}}, 48 * scale);
ImGui_ImplOpenGL3_CreateFontsTexture();
}
int RendererBackend::Run() {
setlocale(LC_ALL, "");
bindtextdomain("neko_player", LOCALE_DIR);
textdomain("neko_player");
printf("Loaded locale '%s' from '%s'...\n", CURRENT_LANGUAGE, LOCALE_DIR);
printf("Locale name: %s\n", _TR_CTX("Language name", "English (United States)"));
bool enable_kms = std::getenv("LAP_KMS") != nullptr;
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "false");
SDL_SetHint(SDL_HINT_APP_NAME, NAME);
// Setup SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0)
{
printf("Error: %s\n", SDL_GetError());
return -1;
}
if (std::string(SDL_GetCurrentVideoDriver()) == "KMSDRM") {
enable_kms = true;
}
IMG_Init(IMG_INIT_PNG|IMG_INIT_WEBP);
prefPath = SDL_GetPrefPath("Catmeow72", NAME);
Theme::prefPath = prefPath;
// Decide GL+GLSL versions
#if defined(IMGUI_IMPL_OPENGL_ES2)
// GL ES 2.0 + GLSL 100
const char* glsl_version = "#version 100";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#elif defined(__APPLE__)
// GL 3.2 Core + GLSL 150
const char* glsl_version = "#version 150";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
#else
// GL 3.0 + GLSL 130
const char* glsl_version = "#version 130";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#endif
// From 2.0.18: Enable native IME.
#ifdef SDL_HINT_IME_SHOW_UI
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
#endif
// Create window with graphics context
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
window = SDL_CreateWindow(NAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_width, window_height, window_flags);
SDL_SetWindowMinimumSize(window, window_width, window_height);
if (enable_kms) {
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
}
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_SetWindowIcon(window, icon);
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, gl_context);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
//io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
io.IniFilename = strdup((std::string(prefPath) + "imgui.ini").c_str());
if (enable_kms) {
io.MouseDrawCursor = true;
}
//io.ConfigViewportsNoAutoMerge = true;
//io.ConfigViewportsNoTaskBarIcon = true;
//ImGui::StyleColorsLight();
// Setup Platform/Renderer backends
ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
ImGui_ImplOpenGL3_Init(glsl_version);
// Load Fonts
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
// - Read 'docs/FONTS.md' for more instructions and details.
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
//io.Fonts->AddFontDefault();
//io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
//IM_ASSERT(font != nullptr);
UpdateScale();
theme = new Theme(false);
userdir = std::getenv(
#ifdef _WIN32
"UserProfile"
#else
"HOME"
#endif
);
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
theme->Apply(accent_color);
Init();
SDL_ShowWindow(window);
#ifdef __EMSCRIPTEN__
// For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file.
// You may manually call LoadIniSettingsFromMemory() to load settings from your own storage.
io.IniFilename = nullptr;
EMSCRIPTEN_MAINLOOP_BEGIN
#else
while (!done)
#endif
{
auto next_frame = std::chrono::steady_clock::now() + std::chrono::milliseconds(1000 / framerate);
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
SDL_Event event;
while (SDL_PollEvent(&event))
{
ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_QUIT)
done = true;
if (event.type == SDL_WINDOWEVENT) {
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
window_width = event.window.data1 / scale;
window_height = event.window.data2 / scale;
//SDL_GetWindowSize(window, &window_width, &window_height);
}
if (event.window.event == SDL_WINDOWEVENT_DISPLAY_CHANGED) {
UpdateScale();
}
if (event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) {
done = true;
}
}
if (event.type == SDL_DROPFILE) {
if (event.drop.file != NULL) {
Drop(std::string(event.drop.file));
free(event.drop.file);
}
}
}
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
// Run the GUI
GuiFunction();
// Rendering
ImGui::Render();
// Update the window size.
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
// Clear the screen.
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
glClear(GL_COLOR_BUFFER_BIT);
// Tell ImGui to render.
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
// Update and Render additional Platform Windows
// (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere.
// For this specific demo app we could also call SDL_GL_MakeCurrent(window, gl_context) directly)
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow();
SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext();
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
SDL_GL_MakeCurrent(backup_current_window, backup_current_context);
}
// Swap the buffers, and do VSync if enabled.
SDL_GL_SwapWindow(window);
// If not doing VSync, wait until the next frame needs to be rendered.
if (!vsync) {
std::this_thread::sleep_until(next_frame);
}
}
// Cleanup
#ifdef __EMSCRIPTEN__
EMSCRIPTEN_MAINLOOP_END;
#endif
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_GL_DeleteContext(gl_context);
SDL_DestroyWindow(window);
IMG_Quit();
SDL_Quit();
free((void*)io.IniFilename);
Deinit();
return 0;
}
void RendererBackend::Init() {
// Do nothing by default.
}
void RendererBackend::Deinit() {
// Do nothing by default.
}
void RendererBackend::Drop(std::string file) {
// Do nothing by default.
}

42
RendererBackend.h Normal file
View file

@ -0,0 +1,42 @@
#pragma once
#include "imgui.h"
#include <functional>
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <SDL_opengles2.h>
#else
#include <SDL_opengl.h>
#endif
#include <SDL.h>
#include <SDL_video.h>
#include <string>
#include "theme.h"
static const char* NAME = "Neko Player";
class RendererBackend {
public:
double scale = 1.0;
SDL_Window *window;
int window_width = 475;
int window_height = 354;
bool done = false;
Theme *theme;
bool vsync = false;
std::string lang;
std::string userdir;
int framerate = 60;
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
ImFont *title;
const char *prefPath;
ImVec4 accent_color = ImVec4(280.0, 1.0, 1.0, 1.0);
int Run();
void SetWindowTitle(const char *title);
virtual void Init();
virtual void GuiFunction();
virtual void Deinit();
virtual void Drop(std::string file);
void UpdateScale();
void AddFonts();
void SetWindowSize(int w, int h);
void GetWindowsize(int *w, int *h);
RendererBackend();
~RendererBackend();
};

25
assets/licenses/cli11.txt Normal file
View file

@ -0,0 +1,25 @@
CLI11 2.2 Copyright (c) 2017-2023 University of Cincinnati, developed by Henry
Schreiner under NSF AWARD 1414736. All rights reserved.
Redistribution and use in source and binary forms of CLI11, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -3,7 +3,8 @@ Type=Application
Name=Neko Player Name=Neko Player
Comment=An audio player that can properly loop audio files Comment=An audio player that can properly loop audio files
GenericName=Looping audio player GenericName=Looping audio player
Exec=neko-player Exec=neko-player %f
Icon=neko-player Icon=neko-player
MimeType=audio/x-wav;audio/ogg;audio/x-vorbis+ogg;audio/x-opus+ogg;audio/mpeg;audio/flac;audio/xm;audio/x-mod;
StartupWMClass=neko-player StartupWMClass=neko-player
Categories=Audio;AudioVideo; Categories=Audio;AudioVideo;

View file

@ -1,528 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Neko Player\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-24 22:50-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.3.2\n"
"X-Poedit-KeywordsList: _TR;_TRS;_TRIS:2;_TRI:2;_TR_CTX:1c,2;_TRS_CTX:1c,2;_TRIS_CTX:2c,3;_TRI_CTX:2c,3\n"
"X-Poedit-Basepath: ../..\n"
"X-Poedit-SearchPath-0: theme.cpp\n"
"X-Poedit-SearchPath-1: main.cpp\n"
#: main.cpp:226
msgctxt "File dialog title"
msgid "Open..."
msgstr ""
#: main.cpp:227
msgctxt "File dialog filter name"
msgid "Audio files"
msgstr ""
#: main.cpp:350
msgctxt "Main menu"
msgid "File"
msgstr ""
#: main.cpp:351
msgctxt "Main menu > File"
msgid "Open"
msgstr ""
#: main.cpp:354
msgctxt "Main menu > File"
msgid "Quit"
msgstr ""
#: main.cpp:359
msgctxt "Main menu"
msgid "Edit"
msgstr ""
#: main.cpp:360
msgctxt "Main menu > Edit"
msgid "Preferences..."
msgstr ""
#: main.cpp:366
msgctxt "Main menu (in debug builds)"
msgid "Debug"
msgstr ""
#: main.cpp:367
msgctxt "Main menu > Debug"
msgid "Show ImGui Demo Window"
msgstr ""
#: main.cpp:373
msgctxt "Main menu"
msgid "Help"
msgstr ""
#: main.cpp:374
msgctxt "Main menu > Help"
msgid "About"
msgstr ""
#: main.cpp:382
msgctxt "Main window title"
msgid "Player"
msgstr ""
#: main.cpp:411
#, c-format
msgctxt "Playback controls > slider"
msgid "Speed: %.2fx"
msgstr ""
#: main.cpp:415
#, c-format
msgctxt "Playback controls > slider"
msgid "Tempo: %.2fx"
msgstr ""
#: main.cpp:419
#, c-format
msgctxt "Playback controls > slider"
msgid "Pitch: %.2fx"
msgstr ""
#: main.cpp:427
msgctxt "Window title, window opened by menu item"
msgid "Preferences..."
msgstr ""
#: main.cpp:429
msgctxt "Preference > VSync checkbox"
msgid "Enable VSync"
msgstr ""
#: main.cpp:434
#, c-format
msgctxt "Preferences > Framerate slider"
msgid "Max framerate without VSync: %d"
msgstr ""
#: main.cpp:435
msgctxt "Preference > Related non-preference button"
msgid "Theme Editor"
msgstr ""
#: main.cpp:439
msgctxt "Preference > Theme selector > Filter label"
msgid "Filter:"
msgstr ""
#: main.cpp:442
msgctxt "Preferences > Theme selector > Selector label"
msgid "Select a theme..."
msgstr ""
#: main.cpp:478
#, c-format
msgctxt "Preference > Accent hue slider, range 0-360 from HSV algorithm hue component"
msgid "Accent color hue: %.0f°"
msgstr ""
#: main.cpp:486
msgctxt "Window title, window opened by menu item"
msgid "About and Licenses"
msgstr ""
#: main.cpp:488
msgctxt "Application name - Japanese word should remain Japanese and stay the same when translating for stylistic purposes."
msgid "ねこ Player"
msgstr ""
#: main.cpp:492
msgctxt "Application name - Japanese word should be converted to the translated language's characters. Use an empty string to disable for Japanese."
msgid "(Neko Player)"
msgstr ""
#: main.cpp:497
msgctxt "Prefix to the version string in the about window"
msgid "Version "
msgstr ""
#: main.cpp:497
msgctxt "Suffix to the version string in the about window, if needed"
msgid ""
msgstr ""
#: main.cpp:503
msgctxt "Library name"
msgid "SDL Mixer X"
msgstr ""
#: main.cpp:504
msgctxt "Library name"
msgid "JsonCpp"
msgstr ""
#: main.cpp:505
msgctxt "Library name"
msgid "SoundTouch"
msgstr ""
#: main.cpp:506
msgctxt "Library name"
msgid "libintl"
msgstr ""
#: main.cpp:507
msgctxt "Library name"
msgid "Dear ImGui"
msgstr ""
#: main.cpp:508
msgctxt "Library name"
msgid "imgui-filebrowser"
msgstr ""
#: main.cpp:510
msgctxt "Library name"
msgid "libportal"
msgstr ""
#: main.cpp:512
msgctxt "Library name"
msgid "Noto Sans"
msgstr ""
#: main.cpp:513
msgctxt "Library name"
msgid "Fork Awesome"
msgstr ""
#: main.cpp:514
msgctxt "Library name"
msgid "IconFontCppHeaders"
msgstr ""
#: main.cpp:539
msgctxt "Project selector label."
msgid "Project"
msgstr ""
#: main.cpp:554
msgctxt "License viewer label"
msgid "License"
msgstr ""
#: main.cpp:557
#, c-format
msgctxt "License viewer > information above license - string 1: selected project, string 2: SPDX license identifier"
msgid "%s: %s"
msgstr ""
#: theme.cpp:36
msgctxt "Window title"
msgid "Theme Editor"
msgstr ""
#: theme.cpp:40
msgctxt "Theme Editor > preset button"
msgid "Create light"
msgstr ""
#: theme.cpp:49
msgctxt "Theme Editor > preset button"
msgid "Create dark"
msgstr ""
#: theme.cpp:57
msgctxt "Theme Editor > import button. Opens the theme import file dialog"
msgid "Import..."
msgstr ""
#: theme.cpp:58
msgctxt "Theme Editor file dialog title"
msgid "Import theme..."
msgstr ""
#: theme.cpp:59 theme.cpp:73
msgctxt "Theme Editor file dialog filter name"
msgid "Theme JSON files"
msgstr ""
#: theme.cpp:71
msgctxt "Theme Editor > export button. Opens the theme export file dialog"
msgid "Export..."
msgstr ""
#: theme.cpp:72
msgctxt "Theme Editor file dialog title"
msgid "Export theme..."
msgstr ""
#: theme.cpp:95
msgctxt "Theme Editor > button. Reverts to saved file."
msgid "Revert"
msgstr ""
#: theme.cpp:104
msgctxt "Theme Editor > button. Saves the theme to it's current location. Not shown when it doesn't already have one."
msgid "Save"
msgstr ""
#: theme.cpp:108
msgctxt "Theme Editor > button. Opens the theme loading dialog for themes created or imported by the user."
msgid "Load..."
msgstr ""
#: theme.cpp:112
msgctxt "Theme Editor > button. Opens the theme saving dialog for themes created or imported by the user."
msgid "Save as..."
msgstr ""
#: theme.cpp:117
msgctxt "Theme Editor > slider. Simplified frame rounding."
msgid "Frame Rounding"
msgstr ""
#: theme.cpp:119
msgctxt "Theme Editor > checkbox"
msgid "Window Border"
msgstr ""
#: theme.cpp:121
msgctxt "Theme Editor > checkbox"
msgid "Frame Border"
msgstr ""
#: theme.cpp:123
msgctxt "Theme Editor > checkbox"
msgid "Popup Border"
msgstr ""
#: theme.cpp:129
msgctxt "Theme Editor > Tab label"
msgid "Sizes"
msgstr ""
#: theme.cpp:131
msgctxt "Theme Editor > Sizes > Section label"
msgid "Sizing"
msgstr ""
#: theme.cpp:132
msgctxt "Theme Editor > Sizes > Sizing > label of XY sliders"
msgid "Window Padding"
msgstr ""
#: theme.cpp:133
msgctxt "Theme Editor > Sizes > Sizing > label of XY sliders"
msgid "Frame Padding"
msgstr ""
#: theme.cpp:134
msgctxt "Theme Editor > Sizes > Sizing > label of XY sliders"
msgid "Cell Padding"
msgstr ""
#: theme.cpp:135
msgctxt "Theme Editor > Sizes > Sizing > label of XY sliders"
msgid "Item Spacing"
msgstr ""
#: theme.cpp:136
msgctxt "Theme Editor > Sizes > Sizing > label of XY sliders"
msgid "Inner Item Spacing"
msgstr ""
#: theme.cpp:139
msgctxt "Theme Editor > Sizes > Sizing > slider label"
msgid "Scrollbar Size"
msgstr ""
#: theme.cpp:140
msgctxt "Theme Editor > Sizes > Sizing > slider label"
msgid "Minimum Grabber Size"
msgstr ""
#: theme.cpp:141
msgctxt "Theme Editor > Sizes > Sizing > label of XY sliders"
msgid "Separator Text Padding"
msgstr ""
#: theme.cpp:143
msgctxt "Theme Editor > Sizes > Section label"
msgid "Borders"
msgstr ""
#: theme.cpp:144
msgctxt "Theme Editor > Sizes > Borders > slider label"
msgid "Window Border Size"
msgstr ""
#: theme.cpp:145
msgctxt "Theme Editor > Sizes > Borders > slider label"
msgid "Child Border Size"
msgstr ""
#: theme.cpp:146
msgctxt "Theme Editor > Sizes > Borders > slider label"
msgid "Popup Border Size"
msgstr ""
#: theme.cpp:147
msgctxt "Theme Editor > Sizes > Borders > slider label"
msgid "Frame Border Size"
msgstr ""
#: theme.cpp:148
msgctxt "Theme Editor > Sizes > Borders > slider label"
msgid "Tab Border Size"
msgstr ""
#: theme.cpp:149
msgctxt "Theme Editor > Sizes > Borders > slider label"
msgid "Separator Text Border Size"
msgstr ""
#: theme.cpp:151
msgctxt "Theme Editor > Sizes > Section label"
msgid "Rounding"
msgstr ""
#: theme.cpp:152
msgctxt "Theme Editor > Sizes > Rounding > slider label"
msgid "Window Rounding"
msgstr ""
#: theme.cpp:153
msgctxt "Theme Editor > Sizes > Rounding > slider label"
msgid "Child Rounding"
msgstr ""
#: theme.cpp:154
msgctxt "Theme Editor > Sizes > Rounding > slider label"
msgid "Frame Rounding"
msgstr ""
#: theme.cpp:155
msgctxt "Theme Editor > Sizes > Rounding > slider label"
msgid "Popup Rounding"
msgstr ""
#: theme.cpp:156
msgctxt "Theme Editor > Sizes > Rounding > slider label"
msgid "Scrollbar Rounding"
msgstr ""
#: theme.cpp:157
msgctxt "Theme Editor > Sizes > Rounding > slider label"
msgid "Grabber Rounding"
msgstr ""
#: theme.cpp:158
msgctxt "Theme Editor > Sizes > Rounding > slider label"
msgid "Tab Rounding"
msgstr ""
#: theme.cpp:163
msgctxt "Theme Editor > Tab label"
msgid "Colors"
msgstr ""
#: theme.cpp:167
msgctxt "Theme Editor > Colors > text filter"
msgid "Filter colors"
msgstr ""
#: theme.cpp:170
msgctxt "Theme Editor > Colors > preview settings radio button"
msgid "Opaque"
msgstr ""
#: theme.cpp:171
msgctxt "Theme Editor > Colors > preview settings radio button"
msgid "Alpha"
msgstr ""
#: theme.cpp:172
msgctxt "Theme Editor > Colors > preview settings radio button"
msgid "Both"
msgstr ""
#: theme.cpp:183
msgctxt "Theme Editor > Colors > (Any color) > recoloring checkbox"
msgid "Match hue preference"
msgstr ""
#: theme.cpp:220 theme.cpp:223
msgctxt "Theme Editor > Custom modal dialog title"
msgid "Load..."
msgstr ""
#: theme.cpp:226
msgctxt "Theme Editor > Load dialog > Theme selector > filter label"
msgid "Filter:"
msgstr ""
#: theme.cpp:229
msgctxt "Theme Editor > Load dialog > Theme selector > label"
msgid "Available themes..."
msgstr ""
#: theme.cpp:244
msgctxt "Theme Editor > Load dialog > Load button"
msgid "Load"
msgstr ""
#: theme.cpp:256
msgctxt "Theme Editor > Load dialog > Cancel button"
msgid "Cancel"
msgstr ""
#: theme.cpp:265 theme.cpp:268
msgctxt "Theme Editor > Custom modal dialog title"
msgid "Save as..."
msgstr ""
#: theme.cpp:271
msgctxt "Theme Editor > Save as dialog > Theme selector > filter label"
msgid "Filter:"
msgstr ""
#: theme.cpp:274
msgctxt "Theme Editor > Save as dialog > Theme selector > label"
msgid "Available themes..."
msgstr ""
#: theme.cpp:290
msgctxt "Theme Editor > Save as dialog > Theme name input label"
msgid "Theme name: "
msgstr ""
#: theme.cpp:291
msgctxt "Theme Editor > Save as dialog > Save button"
msgid "Save"
msgstr ""
#: theme.cpp:302
msgctxt "Theme Editor > Save as dialog > Cancel button"
msgid "Cancel"
msgstr ""
#: theme.cpp:451
msgctxt "Theme default strings > name"
msgid "A theme"
msgstr ""
#: theme.cpp:452
msgctxt "Theme default strings > description"
msgid "(No description)"
msgstr ""

View file

@ -0,0 +1,583 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: Neko Player\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-25 14:02-0700\n"
"PO-Revision-Date: 2023-08-25 14:06-0700\n"
"Last-Translator: \n"
"Language-Team: None\n"
"Language: es_MX\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.3.2\n"
"X-Poedit-KeywordsList: _TR;_TRS;_TRIS:2;_TRI:2;_TR_CTX:1c,2;_TRS_CTX:1c,2;"
"_TRIS_CTX:2c,3;_TRI_CTX:2c,3\n"
"X-Poedit-Basepath: ../..\n"
"X-Poedit-SearchPath-0: theme.cpp\n"
"X-Poedit-SearchPath-1: main.cpp\n"
#: main.cpp:120
msgctxt "Language name"
msgid "English (United States)"
msgstr "English (United States)"
#: main.cpp:292
msgctxt "Built-in themes | Theme default strings | name"
msgid "(built-in)"
msgstr "(incorporado)"
#: main.cpp:296
msgctxt "Built-in light theme | Theme default strings | name"
msgid "Default light"
msgstr "Luz por defecto"
#: main.cpp:304
msgctxt "Built-in dark theme | Theme default strings | name"
msgid "Default dark"
msgstr "Oscuro por defecto"
#: main.cpp:368
msgctxt "Main menu"
msgid "File"
msgstr "Archivo"
#: main.cpp:369
msgctxt "Main menu | File"
msgid "Open"
msgstr "Abrir"
#: main.cpp:371
msgctxt "File dialog title"
msgid "Open..."
msgstr "Abre..."
#: main.cpp:372
msgctxt "File dialog filter name"
msgid "Audio files"
msgstr "Archivos de sonido"
#: main.cpp:375
msgctxt "Main menu | File"
msgid "Quit"
msgstr "Abandonar"
#: main.cpp:380
msgctxt "Main menu"
msgid "Edit"
msgstr "Editar"
#: main.cpp:381
msgctxt "Main menu | Edit"
msgid "Preferences..."
msgstr "Preferencias..."
#: main.cpp:387
msgctxt "Main menu (in debug builds)"
msgid "Debug"
msgstr "Depurar"
#: main.cpp:388
msgctxt "Main menu | Debug"
msgid "Show ImGui Demo Window"
msgstr "Mostrar ventana de demostración de ImGui"
#: main.cpp:394
msgctxt "Main menu"
msgid "Help"
msgstr "Ayudar"
#: main.cpp:395
msgctxt "Main menu | Help"
msgid "About"
msgstr "Sobre"
#: main.cpp:403
msgctxt "Main window title"
msgid "Player"
msgstr "Jugador"
#: main.cpp:432
#, c-format
msgctxt "Playback controls | slider"
msgid "Speed: %.2fx"
msgstr "Velocidad: %.2fx"
#: main.cpp:436
#, c-format
msgctxt "Playback controls | slider"
msgid "Tempo: %.2fx"
msgstr "Tempo: %.2fx"
#: main.cpp:440
#, c-format
msgctxt "Playback controls | slider"
msgid "Pitch: %.2fx"
msgstr "Tono: %.2fx"
#: main.cpp:448
msgctxt "Window title, window opened by menu item"
msgid "Preferences..."
msgstr "Preferencias..."
#: main.cpp:450
msgctxt "Preference | VSync checkbox"
msgid "Enable VSync"
msgstr "Habilitar VSync"
#: main.cpp:455
#, c-format
msgctxt "Preferences | Framerate slider"
msgid "Max framerate without VSync: %d"
msgstr "Frecuencia de imagen máxima sin VSync: %d"
#: main.cpp:456
msgctxt "Preference | Related non-preference button"
msgid "Theme Editor"
msgstr "Editor de temas"
#: main.cpp:460
msgctxt "Preference | override enable checkbox"
msgid "Override language"
msgstr "Anular el idioma"
#: main.cpp:476
msgctxt "Preference | Theme selector | Filter label"
msgid "Filter:"
msgstr "Filtro:"
#: main.cpp:479
msgctxt "Preferences | Theme selector | Selector label"
msgid "Select a theme..."
msgstr "Selecciona un tema..."
#: main.cpp:516
#, c-format
msgctxt ""
"Preference | Accent hue slider, range 0-360 from HSV algorithm hue component"
msgid "Accent color hue: %.0f°"
msgstr "Tono de color de acento %.0f°"
#: main.cpp:524
msgctxt "Window title, window opened by menu item"
msgid "About and Licenses"
msgstr "Sobre y Licencias"
#: main.cpp:526
msgctxt ""
"Application name - Japanese word should remain Japanese and stay the same "
"when translating for stylistic purposes."
msgid "ねこ Player"
msgstr "ねこ Jugador"
#: main.cpp:530
msgctxt ""
"Application name - Japanese word should be converted to the translated "
"language's characters. Use a string with only a single underscore to disable "
"for Japanese."
msgid "(Neko Player)"
msgstr "(Neko Jugador)"
#: main.cpp:535
msgctxt "Version string format specifier"
msgid "Version "
msgstr "Versión"
#: main.cpp:535
msgctxt "Suffix to the version string in the about window, if needed"
msgid " "
msgstr ""
#: main.cpp:541
msgctxt "Library name"
msgid "SDL Mixer X"
msgstr "SDL Mixer X"
#: main.cpp:542
msgctxt "Library name"
msgid "JsonCpp"
msgstr "JsonCpp"
#: main.cpp:543
msgctxt "Library name"
msgid "SoundTouch"
msgstr "SoundTouch"
#: main.cpp:544
msgctxt "Library name"
msgid "libintl"
msgstr "libintl"
#: main.cpp:545
msgctxt "Library name"
msgid "Dear ImGui"
msgstr "Dear ImGui"
#: main.cpp:546
msgctxt "Library name"
msgid "imgui-filebrowser"
msgstr "imgui-filebrowser"
#: main.cpp:548
msgctxt "Library name"
msgid "libportal"
msgstr "libportal"
#: main.cpp:550
msgctxt "Library name"
msgid "Noto Sans"
msgstr "Noto Sans"
#: main.cpp:551
msgctxt "Library name"
msgid "Fork Awesome"
msgstr "Fork Awesome"
#: main.cpp:552
msgctxt "Library name"
msgid "IconFontCppHeaders"
msgstr "Fork Awesome"
#: main.cpp:577
msgctxt "Project selector label."
msgid "Project"
msgstr "Proyecto"
#: main.cpp:592
msgctxt "License viewer label"
msgid "License"
msgstr "Licensia"
#: main.cpp:595
#, c-format
msgctxt ""
"License viewer | information above license - string 1: selected project, "
"string 2: SPDX license identifier"
msgid "%s: %s"
msgstr "%s: %s"
#: theme.cpp:39
msgctxt "Window title"
msgid "Theme Editor"
msgstr "Editor de temas"
#: theme.cpp:43
msgctxt "Theme Editor | preset button"
msgid "Create light"
msgstr "Crear luz"
#: theme.cpp:52
msgctxt "Theme Editor | preset button"
msgid "Create dark"
msgstr "Crear oscuro"
#: theme.cpp:60
msgctxt "Theme Editor | import button. Opens the theme import file dialog"
msgid "Import..."
msgstr "Importar..."
#: theme.cpp:61
msgctxt "Theme Editor file dialog title"
msgid "Import theme..."
msgstr "Importar tema..."
#: theme.cpp:62 theme.cpp:76
msgctxt "Theme Editor file dialog filter name"
msgid "Theme JSON files"
msgstr "Archivos JSON de tema"
#: theme.cpp:74
msgctxt "Theme Editor | export button. Opens the theme export file dialog"
msgid "Export..."
msgstr "Exportar..."
#: theme.cpp:75
msgctxt "Theme Editor file dialog title"
msgid "Export theme..."
msgstr "Exportar tema..."
#: theme.cpp:98
msgctxt "Theme Editor | button. Reverts to saved file."
msgid "Revert"
msgstr "Revertir"
#: theme.cpp:107
msgctxt ""
"Theme Editor | button. Saves the theme to it's current location. Not shown "
"when it doesn't already have one."
msgid "Save"
msgstr "Ahorrar"
#: theme.cpp:111
msgctxt ""
"Theme Editor | button. Opens the theme loading dialog for themes created or "
"imported by the user."
msgid "Load..."
msgstr "Cargar..."
#: theme.cpp:115
msgctxt ""
"Theme Editor | button. Opens the theme saving dialog for themes created or "
"imported by the user."
msgid "Save as..."
msgstr "Guardar como..."
#: theme.cpp:120
msgctxt "Theme Editor | slider. Simplified frame rounding."
msgid "Frame Rounding"
msgstr "Redondeo de marco"
#: theme.cpp:122
msgctxt "Theme Editor | checkbox"
msgid "Window Border"
msgstr "Borde de ventana"
#: theme.cpp:124
msgctxt "Theme Editor | checkbox"
msgid "Frame Border"
msgstr "Borde de"
#: theme.cpp:126
msgctxt "Theme Editor | checkbox"
msgid "Popup Border"
msgstr "Borde emergente"
#: theme.cpp:132
msgctxt "Theme Editor | Tab label"
msgid "Strings"
msgstr "Cadenas"
#: theme.cpp:134
msgctxt "Theme Editor | Strings | Name input label"
msgid "Name"
msgstr "Nombre"
#: theme.cpp:135
msgctxt "Theme Editor | Strings | Description input label"
msgid "Description"
msgstr "Descripción"
#: theme.cpp:139
msgctxt "Theme Editor | Tab label"
msgid "Sizes"
msgstr "Tallas"
#: theme.cpp:141
msgctxt "Theme Editor | Sizes | Section label"
msgid "Sizing"
msgstr "Dimensionamiento"
#: theme.cpp:142
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Window Padding"
msgstr "Acolchado de ventana"
#: theme.cpp:143
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Frame Padding"
msgstr "Acolchado del marco"
#: theme.cpp:144
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Cell Padding"
msgstr "Relleno de celda"
#: theme.cpp:145
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Item Spacing"
msgstr "Espaciado de elementos"
#: theme.cpp:146
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Inner Item Spacing"
msgstr "Espaciado interior de elementos"
#: theme.cpp:149
msgctxt "Theme Editor | Sizes | Sizing | slider label"
msgid "Scrollbar Size"
msgstr "Tamaño de la barra de desplazamiento"
#: theme.cpp:150
msgctxt "Theme Editor | Sizes | Sizing | slider label"
msgid "Minimum Grabber Size"
msgstr "Tamaño mínimo del capturador"
#: theme.cpp:151
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Separator Text Padding"
msgstr "Relleno de texto separador"
#: theme.cpp:153
msgctxt "Theme Editor | Sizes | Section label"
msgid "Borders"
msgstr "Bordes"
#: theme.cpp:154
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Window Border Size"
msgstr "Tamaño del borde de la ventana"
#: theme.cpp:155
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Child Border Size"
msgstr "Tamaño del borde secundario"
#: theme.cpp:156
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Popup Border Size"
msgstr "Tamaño del borde emergente"
#: theme.cpp:157
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Frame Border Size"
msgstr "Tamaño del borde del marco"
#: theme.cpp:158
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Tab Border Size"
msgstr "Tamaño del borde de la pestaña"
#: theme.cpp:159
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Separator Text Border Size"
msgstr "Tamaño del borde del texto separador"
#: theme.cpp:161
msgctxt "Theme Editor | Sizes | Section label"
msgid "Rounding"
msgstr "Redondeo"
#: theme.cpp:162
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Window Rounding"
msgstr "Redondeo de ventana"
#: theme.cpp:163
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Child Rounding"
msgstr "Redondeo infantil"
#: theme.cpp:164
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Frame Rounding"
msgstr "Redondeo de marco"
#: theme.cpp:165
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Popup Rounding"
msgstr "Redondeo emergente"
#: theme.cpp:166
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Scrollbar Rounding"
msgstr "Redondeo de la barra de desplazamiento"
#: theme.cpp:167
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Grabber Rounding"
msgstr "Redondeo del capturador"
#: theme.cpp:168
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Tab Rounding"
msgstr "Redondeo de pestañas"
#: theme.cpp:173
msgctxt "Theme Editor | Tab label"
msgid "Colors"
msgstr "Colores"
#: theme.cpp:177
msgctxt "Theme Editor | Colors | text filter"
msgid "Filter colors"
msgstr "Filtrar colores"
#: theme.cpp:180
msgctxt "Theme Editor | Colors | preview settings radio button"
msgid "Opaque"
msgstr "Opaco"
#: theme.cpp:181
msgctxt "Theme Editor | Colors | preview settings radio button"
msgid "Alpha"
msgstr "Alfa"
#: theme.cpp:182
msgctxt "Theme Editor | Colors | preview settings radio button"
msgid "Both"
msgstr "ambas"
#: theme.cpp:193
msgctxt "Theme Editor | Colors | (Any color) | recoloring checkbox"
msgid "Match hue preference"
msgstr "Preferencia de tono coincidente"
#: theme.cpp:230 theme.cpp:233
msgctxt "Theme Editor | Custom modal dialog title"
msgid "Load..."
msgstr "Cargar..."
#: theme.cpp:236
msgctxt "Theme Editor | Load dialog | Theme selector | filter label"
msgid "Filter:"
msgstr "Filtro:"
#: theme.cpp:239
msgctxt "Theme Editor | Load dialog | Theme selector | label"
msgid "Available themes..."
msgstr "Teams disponibles..."
#: theme.cpp:255
msgctxt "Theme Editor | Load dialog | Load button"
msgid "Load"
msgstr "Cargar"
#: theme.cpp:267
msgctxt "Theme Editor | Load dialog | Cancel button"
msgid "Cancel"
msgstr "Cancelar"
#: theme.cpp:276 theme.cpp:279
msgctxt "Theme Editor | Custom modal dialog title"
msgid "Save as..."
msgstr "Guardar como"
#: theme.cpp:282
msgctxt "Theme Editor | Save as dialog | Theme selector | filter label"
msgid "Filter:"
msgstr "Filtro:"
#: theme.cpp:285
msgctxt "Theme Editor | Save as dialog | Theme selector | label"
msgid "Available themes..."
msgstr "Teams disponibles..."
#: theme.cpp:302
msgctxt "Theme Editor | Save as dialog | Theme name input label"
msgid "Theme name: "
msgstr "Nombre de tema:"
#: theme.cpp:303
msgctxt "Theme Editor | Save as dialog | Save button"
msgid "Save"
msgstr "Ahorrar"
#: theme.cpp:314
msgctxt "Theme Editor | Save as dialog | Cancel button"
msgid "Cancel"
msgstr "Cancelar"
#: theme.cpp:473
msgctxt "Theme default strings | name"
msgid "A theme"
msgstr "Un tema"
#: theme.cpp:474
msgctxt "Theme default strings | description"
msgid "(No description)"
msgstr "(Sin descripción)"

Binary file not shown.

View file

@ -0,0 +1,585 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# ,fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Neko Player\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-25 14:02-0700\n"
"PO-Revision-Date: 2023-07-25 14:22-0700\n"
"Last-Translator: \n"
"Language-Team: None\n"
"Language: ja_JP\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.3.2\n"
"X-Poedit-KeywordsList: _TR;_TRS;_TRIS:2;_TRI:2;_TR_CTX:1c,2;_TRS_CTX:1c,2;"
"_TRIS_CTX:2c,3;_TRI_CTX:2c,3\n"
"X-Poedit-Basepath: ../..\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-SearchPath-0: theme.cpp\n"
"X-Poedit-SearchPath-1: main.cpp\n"
#: main.cpp:120
msgctxt "Language name"
msgid "English (United States)"
msgstr "Japanese (Japan)"
#: main.cpp:292
msgctxt "Built-in themes | Theme default strings | name"
msgid "(built-in)"
msgstr "(内蔵)"
#: main.cpp:296
msgctxt "Built-in light theme | Theme default strings | name"
msgid "Default light"
msgstr "デフォルトのライト"
#: main.cpp:304
msgctxt "Built-in dark theme | Theme default strings | name"
msgid "Default dark"
msgstr "デフォルトの暗い"
#: main.cpp:368
msgctxt "Main menu"
msgid "File"
msgstr "ファイル"
#: main.cpp:369
msgctxt "Main menu | File"
msgid "Open"
msgstr "開ける"
#: main.cpp:371
msgctxt "File dialog title"
msgid "Open..."
msgstr "開ける..."
#: main.cpp:372
msgctxt "File dialog filter name"
msgid "Audio files"
msgstr "オーディオファイル"
#: main.cpp:375
msgctxt "Main menu | File"
msgid "Quit"
msgstr "辞める"
#: main.cpp:380
msgctxt "Main menu"
msgid "Edit"
msgstr "編集"
#: main.cpp:381
msgctxt "Main menu | Edit"
msgid "Preferences..."
msgstr "設定..."
#: main.cpp:387
msgctxt "Main menu (in debug builds)"
msgid "Debug"
msgstr "デバッグ"
#: main.cpp:388
msgctxt "Main menu | Debug"
msgid "Show ImGui Demo Window"
msgstr "デモウィンドウを表示"
#: main.cpp:394
msgctxt "Main menu"
msgid "Help"
msgstr "ヘルプ"
#: main.cpp:395
msgctxt "Main menu | Help"
msgid "About"
msgstr "に関しては"
#: main.cpp:403
msgctxt "Main window title"
msgid "Player"
msgstr "プレーヤー"
#: main.cpp:432
#, c-format
msgctxt "Playback controls | slider"
msgid "Speed: %.2fx"
msgstr "速度: %.2fx"
#: main.cpp:436
#, c-format
msgctxt "Playback controls | slider"
msgid "Tempo: %.2fx"
msgstr "テン: %.2fx"
#: main.cpp:440
#, c-format
msgctxt "Playback controls | slider"
msgid "Pitch: %.2fx"
msgstr "ピッチ: %.2fx"
#: main.cpp:448
msgctxt "Window title, window opened by menu item"
msgid "Preferences..."
msgstr "設定..."
#: main.cpp:450
msgctxt "Preference | VSync checkbox"
msgid "Enable VSync"
msgstr "VSync を有効にする"
#: main.cpp:455
#, c-format
msgctxt "Preferences | Framerate slider"
msgid "Max framerate without VSync: %d"
msgstr "最大フレームレート: %d"
#: main.cpp:456
msgctxt "Preference | Related non-preference button"
msgid "Theme Editor"
msgstr "テーマエディタ"
#: main.cpp:460
msgctxt "Preference | override enable checkbox"
msgid "Override language"
msgstr "言語を上書きする"
#: main.cpp:476
msgctxt "Preference | Theme selector | Filter label"
msgid "Filter:"
msgstr "フィルター:"
#: main.cpp:479
msgctxt "Preferences | Theme selector | Selector label"
msgid "Select a theme..."
msgstr "テーマを選択する..."
#: main.cpp:516
#, c-format
msgctxt ""
"Preference | Accent hue slider, range 0-360 from HSV algorithm hue component"
msgid "Accent color hue: %.0f°"
msgstr "アクセントカラーの色相: %.0f°"
#: main.cpp:524
msgctxt "Window title, window opened by menu item"
msgid "About and Licenses"
msgstr "ライセンスについて"
#: main.cpp:526
msgctxt ""
"Application name - Japanese word should remain Japanese and stay the same "
"when translating for stylistic purposes."
msgid "ねこ Player"
msgstr "ねこ プレーヤー"
#: main.cpp:530
msgctxt ""
"Application name - Japanese word should be converted to the translated "
"language's characters. Use a string with only a single underscore to disable "
"for Japanese."
msgid "(Neko Player)"
msgstr "_"
#: main.cpp:535
msgctxt "Version string format specifier"
msgid "Version "
msgstr "バージョン "
#: main.cpp:535
msgctxt "Suffix to the version string in the about window, if needed"
msgid " "
msgstr ""
#: main.cpp:541
msgctxt "Library name"
msgid "SDL Mixer X"
msgstr "SDL Mixer X"
#: main.cpp:542
msgctxt "Library name"
msgid "JsonCpp"
msgstr "JsonCpp"
#: main.cpp:543
msgctxt "Library name"
msgid "SoundTouch"
msgstr "SoundTouch"
#: main.cpp:544
msgctxt "Library name"
msgid "libintl"
msgstr "libintl"
#: main.cpp:545
msgctxt "Library name"
msgid "Dear ImGui"
msgstr "Dear ImGui"
#: main.cpp:546
msgctxt "Library name"
msgid "imgui-filebrowser"
msgstr "imgui-filebrowser"
#: main.cpp:548
msgctxt "Library name"
msgid "libportal"
msgstr "libportal"
#: main.cpp:550
msgctxt "Library name"
msgid "Noto Sans"
msgstr "Noto Sans"
#: main.cpp:551
msgctxt "Library name"
msgid "Fork Awesome"
msgstr "Fork Awesome"
#: main.cpp:552
msgctxt "Library name"
msgid "IconFontCppHeaders"
msgstr "IconFontCppHeaders"
#: main.cpp:577
msgctxt "Project selector label."
msgid "Project"
msgstr "プロジェクト"
#: main.cpp:592
msgctxt "License viewer label"
msgid "License"
msgstr "ライセンス"
#: main.cpp:595
#, c-format
msgctxt ""
"License viewer | information above license - string 1: selected project, "
"string 2: SPDX license identifier"
msgid "%s: %s"
msgstr "%s: %s"
#: theme.cpp:39
msgctxt "Window title"
msgid "Theme Editor"
msgstr "テーマエディタ"
#: theme.cpp:43
msgctxt "Theme Editor | preset button"
msgid "Create light"
msgstr "ライトテーマを作成する"
#: theme.cpp:52
msgctxt "Theme Editor | preset button"
msgid "Create dark"
msgstr "ダークテーマを作成する"
#: theme.cpp:60
msgctxt "Theme Editor | import button. Opens the theme import file dialog"
msgid "Import..."
msgstr "インポート..."
#: theme.cpp:61
msgctxt "Theme Editor file dialog title"
msgid "Import theme..."
msgstr "テーマのインポート..."
#: theme.cpp:62 theme.cpp:76
msgctxt "Theme Editor file dialog filter name"
msgid "Theme JSON files"
msgstr "テーマの JSON ファイル"
#: theme.cpp:74
msgctxt "Theme Editor | export button. Opens the theme export file dialog"
msgid "Export..."
msgstr "テーマをエクス..."
#: theme.cpp:75
msgctxt "Theme Editor file dialog title"
msgid "Export theme..."
msgstr "テーマをエクスポート..."
#: theme.cpp:98
msgctxt "Theme Editor | button. Reverts to saved file."
msgid "Revert"
msgstr "元に戻す"
#: theme.cpp:107
msgctxt ""
"Theme Editor | button. Saves the theme to it's current location. Not shown "
"when it doesn't already have one."
msgid "Save"
msgstr "保存"
#: theme.cpp:111
msgctxt ""
"Theme Editor | button. Opens the theme loading dialog for themes created or "
"imported by the user."
msgid "Load..."
msgstr "オープン..."
#: theme.cpp:115
msgctxt ""
"Theme Editor | button. Opens the theme saving dialog for themes created or "
"imported by the user."
msgid "Save as..."
msgstr "に名前を付けて保存..."
#: theme.cpp:120
msgctxt "Theme Editor | slider. Simplified frame rounding."
msgid "Frame Rounding"
msgstr "フレームの丸め"
#: theme.cpp:122
msgctxt "Theme Editor | checkbox"
msgid "Window Border"
msgstr "ウィンドウの境界線"
#: theme.cpp:124
msgctxt "Theme Editor | checkbox"
msgid "Frame Border"
msgstr "フレームの境界線"
#: theme.cpp:126
msgctxt "Theme Editor | checkbox"
msgid "Popup Border"
msgstr "ポップアップウィンドウの境界線"
#: theme.cpp:132
msgctxt "Theme Editor | Tab label"
msgid "Strings"
msgstr "ストリングス"
#: theme.cpp:134
msgctxt "Theme Editor | Strings | Name input label"
msgid "Name"
msgstr "名前"
#: theme.cpp:135
msgctxt "Theme Editor | Strings | Description input label"
msgid "Description"
msgstr "形容"
#: theme.cpp:139
msgctxt "Theme Editor | Tab label"
msgid "Sizes"
msgstr "サイズ"
#: theme.cpp:141
msgctxt "Theme Editor | Sizes | Section label"
msgid "Sizing"
msgstr "サイズ"
#: theme.cpp:142
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Window Padding"
msgstr "ウィンドウのパディング"
#: theme.cpp:143
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Frame Padding"
msgstr "フレームのパディング"
#: theme.cpp:144
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Cell Padding"
msgstr "セルの余白"
#: theme.cpp:145
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Item Spacing"
msgstr "項目の間隔"
#: theme.cpp:146
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Inner Item Spacing"
msgstr "項目内の間隔"
#: theme.cpp:149
msgctxt "Theme Editor | Sizes | Sizing | slider label"
msgid "Scrollbar Size"
msgstr "スクロールバーのサイズ"
#: theme.cpp:150
msgctxt "Theme Editor | Sizes | Sizing | slider label"
msgid "Minimum Grabber Size"
msgstr "最小グラバーサイズ"
#: theme.cpp:151
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Separator Text Padding"
msgstr "区切りテキストの余白"
#: theme.cpp:153
msgctxt "Theme Editor | Sizes | Section label"
msgid "Borders"
msgstr "国境"
#: theme.cpp:154
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Window Border Size"
msgstr "ウィンドウの境界線のサイズ"
#: theme.cpp:155
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Child Border Size"
msgstr "子ウィンドウの境界線のサイズ"
#: theme.cpp:156
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Popup Border Size"
msgstr "ポップアップウィンドウの境界線のサイズ"
#: theme.cpp:157
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Frame Border Size"
msgstr "枠線サイズ"
#: theme.cpp:158
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Tab Border Size"
msgstr "タブの境界線のサイズ"
#: theme.cpp:159
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Separator Text Border Size"
msgstr "区切りテキストの境界線のサイズ"
#: theme.cpp:161
msgctxt "Theme Editor | Sizes | Section label"
msgid "Rounding"
msgstr "丸め"
#: theme.cpp:162
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Window Rounding"
msgstr "ウィンドウの丸め"
#: theme.cpp:163
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Child Rounding"
msgstr "子ウィンドウの丸め"
#: theme.cpp:164
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Frame Rounding"
msgstr "フレームの丸め"
#: theme.cpp:165
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Popup Rounding"
msgstr "ポップアップウィンドウの丸め"
#: theme.cpp:166
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Scrollbar Rounding"
msgstr "スクロールバーの丸め"
#: theme.cpp:167
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Grabber Rounding"
msgstr "グラバー丸め"
#: theme.cpp:168
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Tab Rounding"
msgstr "タブの丸め"
#: theme.cpp:173
msgctxt "Theme Editor | Tab label"
msgid "Colors"
msgstr "色"
#: theme.cpp:177
msgctxt "Theme Editor | Colors | text filter"
msgid "Filter colors"
msgstr "フィルターの色"
#: theme.cpp:180
msgctxt "Theme Editor | Colors | preview settings radio button"
msgid "Opaque"
msgstr "オペーク"
#: theme.cpp:181
msgctxt "Theme Editor | Colors | preview settings radio button"
msgid "Alpha"
msgstr "アルファ"
#: theme.cpp:182
msgctxt "Theme Editor | Colors | preview settings radio button"
msgid "Both"
msgstr "両方とも"
#: theme.cpp:193
msgctxt "Theme Editor | Colors | (Any color) | recoloring checkbox"
msgid "Match hue preference"
msgstr "色相設定の一致"
#: theme.cpp:230 theme.cpp:233
msgctxt "Theme Editor | Custom modal dialog title"
msgid "Load..."
msgstr "オープン..."
#: theme.cpp:236
msgctxt "Theme Editor | Load dialog | Theme selector | filter label"
msgid "Filter:"
msgstr "フィルター:"
#: theme.cpp:239
msgctxt "Theme Editor | Load dialog | Theme selector | label"
msgid "Available themes..."
msgstr "利用可能なテーマ..."
#: theme.cpp:255
msgctxt "Theme Editor | Load dialog | Load button"
msgid "Load"
msgstr "負荷"
#: theme.cpp:267
msgctxt "Theme Editor | Load dialog | Cancel button"
msgid "Cancel"
msgstr "キャンセル"
#: theme.cpp:276 theme.cpp:279
msgctxt "Theme Editor | Custom modal dialog title"
msgid "Save as..."
msgstr "に名前を付けて保存..."
#: theme.cpp:282
msgctxt "Theme Editor | Save as dialog | Theme selector | filter label"
msgid "Filter:"
msgstr "フィルター:"
#: theme.cpp:285
msgctxt "Theme Editor | Save as dialog | Theme selector | label"
msgid "Available themes..."
msgstr "利用可能なテーマ..."
#: theme.cpp:302
msgctxt "Theme Editor | Save as dialog | Theme name input label"
msgid "Theme name: "
msgstr "テーマ名: "
#: theme.cpp:303
msgctxt "Theme Editor | Save as dialog | Save button"
msgid "Save"
msgstr "保存"
#: theme.cpp:314
msgctxt "Theme Editor | Save as dialog | Cancel button"
msgid "Cancel"
msgstr "キャンセル"
#: theme.cpp:473
msgctxt "Theme default strings | name"
msgid "A theme"
msgstr "テーマ"
#: theme.cpp:474
msgctxt "Theme default strings | description"
msgid "(No description)"
msgstr "(説明なし)"

View file

@ -0,0 +1,568 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Neko Player\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-25 14:02-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.3.2\n"
"X-Poedit-KeywordsList: _TR;_TRS;_TRIS:2;_TRI:2;_TR_CTX:1c,2;_TRS_CTX:1c,2;_TRIS_CTX:2c,3;_TRI_CTX:2c,3\n"
"X-Poedit-Basepath: ../..\n"
"X-Poedit-SearchPath-0: theme.cpp\n"
"X-Poedit-SearchPath-1: main.cpp\n"
#: main.cpp:120
msgctxt "Language name"
msgid "English (United States)"
msgstr ""
#: main.cpp:292
msgctxt "Built-in themes | Theme default strings | name"
msgid "(built-in)"
msgstr ""
#: main.cpp:296
msgctxt "Built-in light theme | Theme default strings | name"
msgid "Default light"
msgstr ""
#: main.cpp:304
msgctxt "Built-in dark theme | Theme default strings | name"
msgid "Default dark"
msgstr ""
#: main.cpp:368
msgctxt "Main menu"
msgid "File"
msgstr ""
#: main.cpp:369
msgctxt "Main menu | File"
msgid "Open"
msgstr ""
#: main.cpp:371
msgctxt "File dialog title"
msgid "Open..."
msgstr ""
#: main.cpp:372
msgctxt "File dialog filter name"
msgid "Audio files"
msgstr ""
#: main.cpp:375
msgctxt "Main menu | File"
msgid "Quit"
msgstr ""
#: main.cpp:380
msgctxt "Main menu"
msgid "Edit"
msgstr ""
#: main.cpp:381
msgctxt "Main menu | Edit"
msgid "Preferences..."
msgstr ""
#: main.cpp:387
msgctxt "Main menu (in debug builds)"
msgid "Debug"
msgstr ""
#: main.cpp:388
msgctxt "Main menu | Debug"
msgid "Show ImGui Demo Window"
msgstr ""
#: main.cpp:394
msgctxt "Main menu"
msgid "Help"
msgstr ""
#: main.cpp:395
msgctxt "Main menu | Help"
msgid "About"
msgstr ""
#: main.cpp:403
msgctxt "Main window title"
msgid "Player"
msgstr ""
#: main.cpp:432
#, c-format
msgctxt "Playback controls | slider"
msgid "Speed: %.2fx"
msgstr ""
#: main.cpp:436
#, c-format
msgctxt "Playback controls | slider"
msgid "Tempo: %.2fx"
msgstr ""
#: main.cpp:440
#, c-format
msgctxt "Playback controls | slider"
msgid "Pitch: %.2fx"
msgstr ""
#: main.cpp:448
msgctxt "Window title, window opened by menu item"
msgid "Preferences..."
msgstr ""
#: main.cpp:450
msgctxt "Preference | VSync checkbox"
msgid "Enable VSync"
msgstr ""
#: main.cpp:455
#, c-format
msgctxt "Preferences | Framerate slider"
msgid "Max framerate without VSync: %d"
msgstr ""
#: main.cpp:456
msgctxt "Preference | Related non-preference button"
msgid "Theme Editor"
msgstr ""
#: main.cpp:460
msgctxt "Preference | override enable checkbox"
msgid "Override language"
msgstr ""
#: main.cpp:476
msgctxt "Preference | Theme selector | Filter label"
msgid "Filter:"
msgstr ""
#: main.cpp:479
msgctxt "Preferences | Theme selector | Selector label"
msgid "Select a theme..."
msgstr ""
#: main.cpp:516
#, c-format
msgctxt "Preference | Accent hue slider, range 0-360 from HSV algorithm hue component"
msgid "Accent color hue: %.0f°"
msgstr ""
#: main.cpp:524
msgctxt "Window title, window opened by menu item"
msgid "About and Licenses"
msgstr ""
#: main.cpp:526
msgctxt "Application name - Japanese word should remain Japanese and stay the same when translating for stylistic purposes."
msgid "ねこ Player"
msgstr ""
#: main.cpp:530
msgctxt "Application name - Japanese word should be converted to the translated language's characters. Use a string with only a single underscore to disable for Japanese."
msgid "(Neko Player)"
msgstr ""
#: main.cpp:535
msgctxt "Version string format specifier"
msgid "Version "
msgstr ""
#: main.cpp:535
msgctxt "Suffix to the version string in the about window, if needed"
msgid " "
msgstr ""
#: main.cpp:541
msgctxt "Library name"
msgid "SDL Mixer X"
msgstr ""
#: main.cpp:542
msgctxt "Library name"
msgid "JsonCpp"
msgstr ""
#: main.cpp:543
msgctxt "Library name"
msgid "SoundTouch"
msgstr ""
#: main.cpp:544
msgctxt "Library name"
msgid "libintl"
msgstr ""
#: main.cpp:545
msgctxt "Library name"
msgid "Dear ImGui"
msgstr ""
#: main.cpp:546
msgctxt "Library name"
msgid "imgui-filebrowser"
msgstr ""
#: main.cpp:548
msgctxt "Library name"
msgid "libportal"
msgstr ""
#: main.cpp:550
msgctxt "Library name"
msgid "Noto Sans"
msgstr ""
#: main.cpp:551
msgctxt "Library name"
msgid "Fork Awesome"
msgstr ""
#: main.cpp:552
msgctxt "Library name"
msgid "IconFontCppHeaders"
msgstr ""
#: main.cpp:577
msgctxt "Project selector label."
msgid "Project"
msgstr ""
#: main.cpp:592
msgctxt "License viewer label"
msgid "License"
msgstr ""
#: main.cpp:595
#, c-format
msgctxt "License viewer | information above license - string 1: selected project, string 2: SPDX license identifier"
msgid "%s: %s"
msgstr ""
#: theme.cpp:39
msgctxt "Window title"
msgid "Theme Editor"
msgstr ""
#: theme.cpp:43
msgctxt "Theme Editor | preset button"
msgid "Create light"
msgstr ""
#: theme.cpp:52
msgctxt "Theme Editor | preset button"
msgid "Create dark"
msgstr ""
#: theme.cpp:60
msgctxt "Theme Editor | import button. Opens the theme import file dialog"
msgid "Import..."
msgstr ""
#: theme.cpp:61
msgctxt "Theme Editor file dialog title"
msgid "Import theme..."
msgstr ""
#: theme.cpp:62 theme.cpp:76
msgctxt "Theme Editor file dialog filter name"
msgid "Theme JSON files"
msgstr ""
#: theme.cpp:74
msgctxt "Theme Editor | export button. Opens the theme export file dialog"
msgid "Export..."
msgstr ""
#: theme.cpp:75
msgctxt "Theme Editor file dialog title"
msgid "Export theme..."
msgstr ""
#: theme.cpp:98
msgctxt "Theme Editor | button. Reverts to saved file."
msgid "Revert"
msgstr ""
#: theme.cpp:107
msgctxt "Theme Editor | button. Saves the theme to it's current location. Not shown when it doesn't already have one."
msgid "Save"
msgstr ""
#: theme.cpp:111
msgctxt "Theme Editor | button. Opens the theme loading dialog for themes created or imported by the user."
msgid "Load..."
msgstr ""
#: theme.cpp:115
msgctxt "Theme Editor | button. Opens the theme saving dialog for themes created or imported by the user."
msgid "Save as..."
msgstr ""
#: theme.cpp:120
msgctxt "Theme Editor | slider. Simplified frame rounding."
msgid "Frame Rounding"
msgstr ""
#: theme.cpp:122
msgctxt "Theme Editor | checkbox"
msgid "Window Border"
msgstr ""
#: theme.cpp:124
msgctxt "Theme Editor | checkbox"
msgid "Frame Border"
msgstr ""
#: theme.cpp:126
msgctxt "Theme Editor | checkbox"
msgid "Popup Border"
msgstr ""
#: theme.cpp:132
msgctxt "Theme Editor | Tab label"
msgid "Strings"
msgstr ""
#: theme.cpp:134
msgctxt "Theme Editor | Strings | Name input label"
msgid "Name"
msgstr ""
#: theme.cpp:135
msgctxt "Theme Editor | Strings | Description input label"
msgid "Description"
msgstr ""
#: theme.cpp:139
msgctxt "Theme Editor | Tab label"
msgid "Sizes"
msgstr ""
#: theme.cpp:141
msgctxt "Theme Editor | Sizes | Section label"
msgid "Sizing"
msgstr ""
#: theme.cpp:142
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Window Padding"
msgstr ""
#: theme.cpp:143
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Frame Padding"
msgstr ""
#: theme.cpp:144
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Cell Padding"
msgstr ""
#: theme.cpp:145
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Item Spacing"
msgstr ""
#: theme.cpp:146
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Inner Item Spacing"
msgstr ""
#: theme.cpp:149
msgctxt "Theme Editor | Sizes | Sizing | slider label"
msgid "Scrollbar Size"
msgstr ""
#: theme.cpp:150
msgctxt "Theme Editor | Sizes | Sizing | slider label"
msgid "Minimum Grabber Size"
msgstr ""
#: theme.cpp:151
msgctxt "Theme Editor | Sizes | Sizing | label of XY sliders"
msgid "Separator Text Padding"
msgstr ""
#: theme.cpp:153
msgctxt "Theme Editor | Sizes | Section label"
msgid "Borders"
msgstr ""
#: theme.cpp:154
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Window Border Size"
msgstr ""
#: theme.cpp:155
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Child Border Size"
msgstr ""
#: theme.cpp:156
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Popup Border Size"
msgstr ""
#: theme.cpp:157
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Frame Border Size"
msgstr ""
#: theme.cpp:158
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Tab Border Size"
msgstr ""
#: theme.cpp:159
msgctxt "Theme Editor | Sizes | Borders | slider label"
msgid "Separator Text Border Size"
msgstr ""
#: theme.cpp:161
msgctxt "Theme Editor | Sizes | Section label"
msgid "Rounding"
msgstr ""
#: theme.cpp:162
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Window Rounding"
msgstr ""
#: theme.cpp:163
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Child Rounding"
msgstr ""
#: theme.cpp:164
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Frame Rounding"
msgstr ""
#: theme.cpp:165
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Popup Rounding"
msgstr ""
#: theme.cpp:166
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Scrollbar Rounding"
msgstr ""
#: theme.cpp:167
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Grabber Rounding"
msgstr ""
#: theme.cpp:168
msgctxt "Theme Editor | Sizes | Rounding | slider label"
msgid "Tab Rounding"
msgstr ""
#: theme.cpp:173
msgctxt "Theme Editor | Tab label"
msgid "Colors"
msgstr ""
#: theme.cpp:177
msgctxt "Theme Editor | Colors | text filter"
msgid "Filter colors"
msgstr ""
#: theme.cpp:180
msgctxt "Theme Editor | Colors | preview settings radio button"
msgid "Opaque"
msgstr ""
#: theme.cpp:181
msgctxt "Theme Editor | Colors | preview settings radio button"
msgid "Alpha"
msgstr ""
#: theme.cpp:182
msgctxt "Theme Editor | Colors | preview settings radio button"
msgid "Both"
msgstr ""
#: theme.cpp:193
msgctxt "Theme Editor | Colors | (Any color) | recoloring checkbox"
msgid "Match hue preference"
msgstr ""
#: theme.cpp:230 theme.cpp:233
msgctxt "Theme Editor | Custom modal dialog title"
msgid "Load..."
msgstr ""
#: theme.cpp:236
msgctxt "Theme Editor | Load dialog | Theme selector | filter label"
msgid "Filter:"
msgstr ""
#: theme.cpp:239
msgctxt "Theme Editor | Load dialog | Theme selector | label"
msgid "Available themes..."
msgstr ""
#: theme.cpp:255
msgctxt "Theme Editor | Load dialog | Load button"
msgid "Load"
msgstr ""
#: theme.cpp:267
msgctxt "Theme Editor | Load dialog | Cancel button"
msgid "Cancel"
msgstr ""
#: theme.cpp:276 theme.cpp:279
msgctxt "Theme Editor | Custom modal dialog title"
msgid "Save as..."
msgstr ""
#: theme.cpp:282
msgctxt "Theme Editor | Save as dialog | Theme selector | filter label"
msgid "Filter:"
msgstr ""
#: theme.cpp:285
msgctxt "Theme Editor | Save as dialog | Theme selector | label"
msgid "Available themes..."
msgstr ""
#: theme.cpp:302
msgctxt "Theme Editor | Save as dialog | Theme name input label"
msgid "Theme name: "
msgstr ""
#: theme.cpp:303
msgctxt "Theme Editor | Save as dialog | Save button"
msgid "Save"
msgstr ""
#: theme.cpp:314
msgctxt "Theme Editor | Save as dialog | Cancel button"
msgid "Cancel"
msgstr ""
#: theme.cpp:473
msgctxt "Theme default strings | name"
msgid "A theme"
msgstr ""
#: theme.cpp:474
msgctxt "Theme default strings | description"
msgid "(No description)"
msgstr ""

View file

@ -34,6 +34,7 @@ add_basic 'licenses/SoundTouch.txt' 'soundtouch_license'
add_basic 'licenses/libportal.txt' 'libportal_license' add_basic 'licenses/libportal.txt' 'libportal_license'
add_basic 'licenses/ForkAwesome.txt' 'forkawesome_license' add_basic 'licenses/ForkAwesome.txt' 'forkawesome_license'
add_basic 'licenses/libintl.txt' 'libintl_license' add_basic 'licenses/libintl.txt' 'libintl_license'
add_basic 'licenses/cli11.txt' 'cli11_license'
add_basic '../IconFontCppHeaders/licence.txt' 'icnfntcpphdrs_license' add_basic '../IconFontCppHeaders/licence.txt' 'icnfntcpphdrs_license'
echo '#pragma once' > 'assets.h' echo '#pragma once' > 'assets.h'
for i in "${ASSETS[@]}"; do for i in "${ASSETS[@]}"; do

9
conanfile.txt Normal file
View file

@ -0,0 +1,9 @@
[requires]
sdl2/2.28.3
SDL2_image/2.0.5
soundtouch/2.3.2
libgettext/0.22
[generators]
PkgConfigDeps
MesonToolchain

131
imgui_config.h Normal file
View file

@ -0,0 +1,131 @@
//-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
//-----------------------------------------------------------------------------
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
#ifdef IMGUI_IMPL_OPENGL_ES2
#undef IMGUI_IMPL_OPENGL_ES2
#include <SDL_opengles2.h>
#else
#define IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#define GL_GLEXT_PROTOTYPES
#include <SDL_opengl.h>
#include <SDL_opengl_glext.h>
#endif
//---- Define assertion handler. Defaults to calling assert().
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88).
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
//---- Include imgui_user.h at the end of imgui.h as a convenience
//#define IMGUI_INCLUDE_IMGUI_USER_H
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
//#define IMGUI_USE_WCHAR32
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
//#define IMGUI_USE_STB_SPRINTF
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
//#define IMGUI_ENABLE_FREETYPE
//---- Use stb_truetype to build and rasterize the font atlas (default)
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
//#define IMGUI_ENABLE_STB_TRUETYPE
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
/*
#define IM_VEC2_CLASS_EXTRA \
constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \
operator MyVec2() const { return MyVec2(x,y); }
#define IM_VEC4_CLASS_EXTRA \
constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
operator MyVec4() const { return MyVec4(x,y,z,w); }
*/
//---- ...Or use Dear ImGui's own very basic math operators.
//#define IMGUI_DEFINE_MATH_OPERATORS
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
//#define ImDrawIdx unsigned int
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
//struct ImDrawList;
//struct ImDrawCmd;
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback
//---- Debug Tools: Macro to break in Debugger
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
//---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
/*
namespace ImGui
{
void MyFunction(const char* name, const MyMatrix44& v);
}
*/

View file

@ -1,3 +0,0 @@
#!/bin/sh
./build-file.sh
flatpak install --reinstall -y --user --noninteractive ./neko-player.flatpak

848
main.cpp
View file

@ -1,42 +1,6 @@
#include "config.h" #include "main.h"
#include "imgui.h" #include "assets.h"
#include "imgui_stdlib.h" #include "thirdparty/CLI11.hpp"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_opengl3.h"
#include "file_browser.h"
#include "playback.h"
#include "theme.h"
#include "assets.h"
#include "IconsForkAwesome.h"
#include <libintl.h>
#include <iostream>
#include <fstream>
#include <json/json.h>
#include <stdio.h>
#include <numbers>
#include <cmath>
#include <cstdlib>
#include <string>
#include <SDL.h>
#include <SDL_image.h>
#include <filesystem>
#include <SDL_video.h>
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <SDL_opengles2.h>
#else
#include <SDL_opengl.h>
#endif
#include "license.h"
#include "base85.h"
static const char* NAME = "Neko Player";
#ifdef __EMSCRIPTEN__
#include "../libs/emscripten/emscripten_mainloop_stub.h"
#endif
#include <translation.h>
using namespace std::filesystem;
using namespace std::numbers;
using std::string;
static float accent_color = 280.0;
string PadZeros(string input, size_t required_length) { string PadZeros(string input, size_t required_length) {
return std::string(required_length - std::min(required_length, input.length()), '0') + input; return std::string(required_length - std::min(required_length, input.length()), '0') + input;
@ -71,182 +35,24 @@ string TimeToString(double time_code, uint8_t min_components = 1) {
} }
return output; return output;
} }
void MainLoop::Init() {
struct FontData {
const char* data;
const ImWchar *ranges;
};
ImFont *add_font(vector<FontData> data_vec, int size = 13) {
ImFont* font = nullptr;
ImGuiIO& io = ImGui::GetIO();
for (auto data : data_vec) {
ImFontConfig font_cfg = ImFontConfig();
font_cfg.SizePixels = size;
font_cfg.OversampleH = font_cfg.OversampleV = 1;
font_cfg.PixelSnapH = true;
if (font_cfg.SizePixels <= 0.0f)
font_cfg.SizePixels = 13.0f * 1.0f;
if (font != nullptr) {
font_cfg.DstFont = font;
font_cfg.MergeMode = true;
}
//font_cfg.EllipsisChar = (ImWchar)0x0085;
//font_cfg.GlyphOffset.y = 1.0f * IM_FLOOR(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units
const char* ttf_compressed_base85 = data.data;
const ImWchar* glyph_ranges = data.ranges;
auto new_font = io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, glyph_ranges);
if (font == nullptr) font = new_font;
}
{
ImFontConfig config;
config.MergeMode = true;
config.GlyphMinAdvanceX = size;
config.SizePixels = size;
config.DstFont = font;
static const ImWchar icon_ranges[] = { ICON_MIN_FK, ICON_MAX_FK, 0 };
io.Fonts->AddFontFromMemoryCompressedBase85TTF(forkawesome_compressed_data_base85, float(size), &config, icon_ranges);
}
return font;
}
// Main code
int main(int, char**)
{
bindtextdomain("neko_player", LOCALE_DIR);
#ifdef PORTALS #ifdef PORTALS
g_set_application_name("Neko Player"); g_set_application_name("Neko Player");
#endif #endif
bool enable_kms = std::getenv("LAP_KMS") != nullptr;
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "false");
SDL_SetHint(SDL_HINT_APP_NAME, NAME);
// Setup SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0)
{
printf("Error: %s\n", SDL_GetError());
return -1;
}
if (std::string(SDL_GetCurrentVideoDriver()) == "KMSDRM") {
enable_kms = true;
}
IMG_Init(IMG_INIT_PNG|IMG_INIT_WEBP);
const char* prefPath = SDL_GetPrefPath("Catmeow72", NAME);
Theme::prefPath = prefPath;
// Decide GL+GLSL versions
#if defined(IMGUI_IMPL_OPENGL_ES2)
// GL ES 2.0 + GLSL 100
const char* glsl_version = "#version 100";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#elif defined(__APPLE__)
// GL 3.2 Core + GLSL 150
const char* glsl_version = "#version 150";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
#else
// GL 3.0 + GLSL 130
const char* glsl_version = "#version 130";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#endif
// From 2.0.18: Enable native IME.
#ifdef SDL_HINT_IME_SHOW_UI
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
#endif
// Create window with graphics context
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
int window_width = 475;
int window_height = 354;
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_Window* window = SDL_CreateWindow(NAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_width, window_height, window_flags);
SDL_SetWindowMinimumSize(window, window_width, window_height);
if (enable_kms) {
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
}
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_SetWindowIcon(window, icon);
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, gl_context);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
//io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
io.IniFilename = strdup((std::string(prefPath) + "imgui.ini").c_str());
if (enable_kms) {
io.MouseDrawCursor = true;
}
//io.ConfigViewportsNoAutoMerge = true;
//io.ConfigViewportsNoTaskBarIcon = true;
//ImGui::StyleColorsLight();
// Setup Platform/Renderer backends
ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
ImGui_ImplOpenGL3_Init(glsl_version);
// Load Fonts
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
// - Read 'docs/FONTS.md' for more instructions and details.
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
//io.Fonts->AddFontDefault();
//io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
//IM_ASSERT(font != nullptr);
add_font({FontData {notosans_regular_compressed_data_base85, io.Fonts->GetGlyphRangesDefault()}, FontData {notosansjp_regular_compressed_data_base85, io.Fonts->GetGlyphRangesJapanese()}});
ImFont *title = add_font({FontData {notosans_thin_compressed_data_base85, io.Fonts->GetGlyphRangesDefault()}, FontData {notosansjp_thin_compressed_data_base85, io.Fonts->GetGlyphRangesJapanese()}}, 48);
// Our state // Our state
bool show_demo_window = false; show_demo_window = false;
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
FileBrowser fileDialog(false, ImGuiFileBrowserFlags_NoTitleBar|ImGuiFileBrowserFlags_NoMove|ImGuiFileBrowserFlags_NoResize); FileBrowser fileDialog(false, ImGuiFileBrowserFlags_NoTitleBar|ImGuiFileBrowserFlags_NoMove|ImGuiFileBrowserFlags_NoResize);
fileDialog.SetTitle(_TR_CTX("File dialog title", "Open..."));
fileDialog.SetTypeFilters(_TR_CTX("File dialog filter name", "Audio files"), { ".wav", ".ogg", ".mp3", ".qoa", ".flac", ".xm", ".mod"});
std::string userdir = std::getenv(
#ifdef _WIN32
"UserProfile"
#else
"HOME"
#endif
);
fileDialog.SetPwd(path(userdir) / path("Music")); fileDialog.SetPwd(path(userdir) / path("Music"));
fileDialog.SetWindowSize(window_width, window_height); fileDialog.SetWindowSize(window_width, window_height);
//fileDialog.SetWindowPos(0, 0); //fileDialog.SetWindowPos(0, 0);
Playback *playback = new Playback(); playback = new Playback();
float position = 0.0; position = 0.0;
// Main loop prefs_window = false;
bool done = false; theme_editor = false;
Theme *theme = new Theme(false); stopped = true;
bool prefs_window = false; about_window = false;
bool theme_editor = false; string lang;
bool stopped = true;
bool vsync = false;
bool about_window = false;
int framerate = 60;
{ {
Json::Value config; Json::Value config;
std::ifstream stream; std::ifstream stream;
@ -261,7 +67,12 @@ int main(int, char**)
} }
} }
if (config.isMember("accent_color")) { if (config.isMember("accent_color")) {
accent_color = config["accent_color"].asFloat(); if (config["accent_color"].isNumeric()) {
accent_color.x = config["accent_color"].asFloat() / 360.0;
} else {
Json::Value accentColor = config["accent_color"];
accent_color = ImVec4(accentColor["h"].asFloat(), accentColor["s"].asFloat(), accentColor["v"].asFloat(), accentColor["a"].asFloat());
}
} }
if (config.isMember("demo_window")) { if (config.isMember("demo_window")) {
show_demo_window = config["demo_window"].asBool(); show_demo_window = config["demo_window"].asBool();
@ -272,16 +83,25 @@ int main(int, char**)
if (config.isMember("framerate")) { if (config.isMember("framerate")) {
framerate = config["framerate"].asUInt(); framerate = config["framerate"].asUInt();
} }
if (config.isMember("lang")) {
Json::Value langValue;
if (langValue.isNull()) {
lang = DEFAULT_LANG;
} else {
lang = config["lang"].asString();
}
SET_LANG(lang.c_str());
}
stream.close(); stream.close();
} }
if (is_empty(Theme::themeDir)) { if (is_empty(Theme::themeDir)) {
path lightPath = Theme::themeDir / "light.json"; path lightPath = Theme::themeDir / "light.json";
path darkPath = Theme::themeDir / "dark.json"; path darkPath = Theme::themeDir / "dark.json";
string builtinDescription = _TRS_CTX("Built-in themes > Theme default strings > name", "(built-in)"); string builtinDescription = _TRS_CTX("Built-in themes | Theme default strings | name", "(built-in)");
if (!exists(lightPath)) { if (!exists(lightPath)) {
Theme light(false); Theme light(false);
ThemeStrings &strings = light.strings["fallback"]; ThemeStrings &strings = light.strings["fallback"];
strings.name = _TRS_CTX("Built-in light theme > Theme default strings > name", "Default light"); strings.name = _TRS_CTX("Built-in light theme | Theme default strings | name", "Default light");
strings.description = builtinDescription; strings.description = builtinDescription;
light.strings[CURRENT_LANGUAGE] = strings; light.strings[CURRENT_LANGUAGE] = strings;
light.Save(lightPath); light.Save(lightPath);
@ -289,7 +109,7 @@ int main(int, char**)
if (!exists(darkPath)) { if (!exists(darkPath)) {
Theme dark(true); Theme dark(true);
ThemeStrings &strings = dark.strings["fallback"]; ThemeStrings &strings = dark.strings["fallback"];
strings.name = _TRS_CTX("Built-in dark theme > Theme default strings > name", "Default dark"); strings.name = _TRS_CTX("Built-in dark theme | Theme default strings | name", "Default dark");
strings.description = builtinDescription; strings.description = builtinDescription;
dark.strings[CURRENT_LANGUAGE] = strings; dark.strings[CURRENT_LANGUAGE] = strings;
dark.Save(darkPath); dark.Save(darkPath);
@ -298,335 +118,309 @@ int main(int, char**)
theme = new Theme(darkPath); theme = new Theme(darkPath);
} }
} }
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
theme->Apply(accent_color); theme->Apply(accent_color);
#ifdef __EMSCRIPTEN__ CLI::App app{"An audio player that can play files that need special handling to loop seamlessly."};
// For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file. std::string filename = "";
// You may manually call LoadIniSettingsFromMemory() to load settings from your own storage. app.allow_extras();
io.IniFilename = nullptr; double new_speed = 1.0;
EMSCRIPTEN_MAINLOOP_BEGIN double new_tempo = 1.0;
#else double new_pitch = 1.0;
while (!done) app.add_option("-s,--speed", new_speed, "Set the initial speed of the playback.")->default_val(1.0);
#endif app.add_option("-t,--tempo", new_tempo, "Set the initial tempo of the playback.")->default_val(1.0);
{/* app.add_option("-p,--pitch", new_pitch, "Set the initial pitch of the playback.")->default_val(1.0);
{ try {
int min_x; app.parse(args);
int min_y; } catch (const CLI::ParseError &e) {
SDL_GetWindowMinimumSize(window, &min_x, &min_y); exit(app.exit(e));
int height = ImGui::GetFrameHeightWithSpacing() + ImGui::GetFrameHeight() + (ImGui::GetStyle().WindowPadding.y * 2) + ((ImGui::GetStyle().FramePadding.y * 2) + ImGui::GetFontSize()); }
if (height != min_y) { playback->speed = new_speed;
min_y = height; playback->tempo = new_tempo;
SDL_SetWindowMinimumSize(window, 475, min_y); playback->pitch = new_pitch;
args = app.remaining();
if (args.size() > 0) {
LoadFile(args[0]);
}
}
void MainLoop::Drop(std::string file) {
LoadFile(file);
}
void MainLoop::GuiFunction() {
position = playback->GetPosition();
auto dockid = ImGui::DockSpaceOverViewport(nullptr, ImGuiDockNodeFlags_PassthruCentralNode|ImGuiDockNodeFlags_AutoHideTabBar);
// 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window);
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_FILE, "Main menu", "File"))) {
if (ImGui::MenuItem(_TRI_CTX(ICON_FK_FOLDER_OPEN, "Main menu | File", "Open"))) {
// Set translatable strings here so that they are in the correct language even when it changes at runtime.
fileDialog.SetTitle(_TR_CTX("File dialog title", "Open..."));
fileDialog.SetTypeFilters(_TR_CTX("File dialog filter name", "Audio files"), { ".wav", ".ogg", ".mp3", ".qoa", ".flac", ".xm", ".mod"});
fileDialog.Open();
} }
}*/ if (ImGui::MenuItem(_TRI_CTX(ICON_FK_WINDOW_CLOSE, "Main menu | File", "Quit"))) {
auto next_frame = std::chrono::steady_clock::now() + std::chrono::milliseconds(1000 / framerate);
position = playback->GetPosition();
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
SDL_Event event;
while (SDL_PollEvent(&event))
{
ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_QUIT)
done = true; done = true;
if (event.type == SDL_WINDOWEVENT) {
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
window_width = event.window.data1;
window_height = event.window.data2;
//SDL_GetWindowSize(window, &window_width, &window_height);
}
if (event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) {
done = true;
}
} }
ImGui::EndMenu();
} }
if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_SCISSORS,"Main menu", "Edit"))) {
// Start the Dear ImGui frame if (ImGui::MenuItem(_TRI_CTX(ICON_FK_COG, "Main menu | Edit", "Preferences..."))) {
ImGui_ImplOpenGL3_NewFrame(); prefs_window = true;
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
auto dockid = ImGui::DockSpaceOverViewport(nullptr, ImGuiDockNodeFlags_PassthruCentralNode|ImGuiDockNodeFlags_AutoHideTabBar);
// 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window);
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_FILE, "Main menu", "File"))) {
if (ImGui::MenuItem(_TRI_CTX(ICON_FK_FOLDER_OPEN, "Main menu > File", "Open"))) {
fileDialog.Open();
}
if (ImGui::MenuItem(_TRI_CTX(ICON_FK_WINDOW_CLOSE, "Main menu > File", "Quit"))) {
done = true;
}
ImGui::EndMenu();
} }
if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_SCISSORS,"Main menu", "Edit"))) { ImGui::EndMenu();
if (ImGui::MenuItem(_TRI_CTX(ICON_FK_COG, "Main menu > Edit", "Preferences..."))) {
prefs_window = true;
}
ImGui::EndMenu();
}
#ifdef DEBUG
if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_COG, "Main menu (in debug builds)", "Debug"))) {
if (ImGui::MenuItem(_TR_CTX("Main menu > Debug", "Show ImGui Demo Window"), nullptr, show_demo_window)) {
show_demo_window = !show_demo_window;
}
ImGui::EndMenu();
}
#endif
if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_INFO_CIRCLE, "Main menu", "Help"))) {
if (ImGui::MenuItem(_TRI_CTX(ICON_FK_INFO, "Main menu > Help", "About"), nullptr, about_window)) {
about_window = !about_window;
}
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
} }
#ifdef DEBUG
if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_COG, "Main menu (in debug builds)", "Debug"))) {
if (ImGui::MenuItem(_TR_CTX("Main menu | Debug", "Show ImGui Demo Window"), nullptr, show_demo_window)) {
show_demo_window = !show_demo_window;
}
ImGui::EndMenu();
}
#endif
if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_INFO_CIRCLE, "Main menu", "Help"))) {
if (ImGui::MenuItem(_TRI_CTX(ICON_FK_INFO, "Main menu | Help", "About"), nullptr, about_window)) {
about_window = !about_window;
}
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
ImGui::SetNextWindowDockID(dockid);
ImGui::Begin(_TRI_CTX(ICON_FK_PLAY, "Main window title", "Player"), nullptr, 0);
{
ImGui::SetCursorPosY(ImGui::GetWindowHeight() - ImGui::GetFrameHeightWithSpacing() - ImGui::GetFrameHeight() - ImGui::GetStyle().WindowPadding.y);
if (ImGui::Button(playback->IsPaused() ? ICON_FK_PLAY "##Pause" : ICON_FK_PAUSE "##Pause")) {
playback->Pause();
}
ImGui::SameLine();
if (ImGui::Button(ICON_FK_REFRESH "##Restart")) {
playback->Seek(0.0);
}
ImGui::SameLine();
const int NEXT_SLIDER_COUNT = 1;
ImGui::SetNextItemWidth(-(ImGui::GetFontSize() * (1 + (8 * NEXT_SLIDER_COUNT))) - ((ImGui::GetStyle().ItemSpacing.x + ImGui::GetStyle().FramePadding.x) * (NEXT_SLIDER_COUNT + 1)));
uint8_t components = TimeToComponentCount(playback->GetLength());
string time_str = TimeToString(position, components);
if (ImGui::SliderFloat("##Seek", &position, 0.0f, playback->GetLength(), time_str.c_str(), ImGuiSliderFlags_NoRoundToFormat))
playback->Seek(position);
ImGui::SameLine();
if (ImGui::Button(ICON_FK_STOP "##Stop")) {
playback->Stop();
}
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
if (ImGui::SliderFloat("##Volume", &playback->volume, 0.0, 100.0, ICON_FK_VOLUME_UP ": %.0f%%")) {
playback->Update();
}
const float items = 3.0f;
const float between_items = items - 1.0f;
ImGui::PushItemWidth((ImGui::GetWindowWidth() / items) - (ImGui::GetStyle().ItemSpacing.x / (items / between_items)) - ((ImGui::GetStyle().WindowPadding.x / items) * 2.0f));
if (ImGui::SliderFloat("##Speed", &playback->speed, 0.25, 4.0, _TR_CTX("Playback controls | slider", "Speed: %.2fx"), ImGuiSliderFlags_Logarithmic)) {
playback->Update();
}
ImGui::SameLine();
if (ImGui::SliderFloat("##Tempo", &playback->tempo, 0.25, 4.0, _TR_CTX("Playback controls | slider", "Tempo: %.2fx"), ImGuiSliderFlags_Logarithmic)) {
playback->Update();
}
ImGui::SameLine();
if (ImGui::SliderFloat("##Pitch", &playback->pitch, 0.25, 4.0, _TR_CTX("Playback controls | slider", "Pitch: %.2fx"), ImGuiSliderFlags_Logarithmic)) {
playback->Update();
}
ImGui::PopItemWidth();
}
ImGui::End();
if (prefs_window) {
ImGui::SetNextWindowDockID(dockid); ImGui::SetNextWindowDockID(dockid);
ImGui::Begin(_TRI_CTX(ICON_FK_PLAY, "Main window title", "Player"), nullptr, 0); ImGui::Begin(_TRI_CTX(ICON_FK_COG, "Window title, window opened by menu item", "Preferences..."), &prefs_window);
{ {
ImGui::SetCursorPosY(ImGui::GetWindowHeight() - ImGui::GetFrameHeightWithSpacing() - ImGui::GetFrameHeight() - ImGui::GetStyle().WindowPadding.y); if (ImGui::Checkbox(_TR_CTX("Preference | VSync checkbox", "Enable VSync"), &vsync)) {
if (ImGui::Button(playback->IsPaused() ? ICON_FK_PLAY "##Pause" : ICON_FK_PAUSE "##Pause")) { SDL_GL_SetSwapInterval(vsync ? 1 : 0);
playback->Pause();
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(ICON_FK_REFRESH "##Restart")) { ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x);
playback->Seek(0.0); ImGui::SliderInt("##Framerate", &framerate, 10, 480, _TR_CTX("Preferences | Framerate slider", "Max framerate without VSync: %d"));
if (ImGui::Button(_TRI_CTX(ICON_FK_MAGIC, "Preference | Related non-preference button", "Theme Editor"), ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), 0))) {
theme_editor = true;
} }
ImGui::SameLine(); static bool override_lang = lang != DEFAULT_LANG;
const int NEXT_SLIDER_COUNT = 1; if (ImGui::Checkbox(_TR_CTX("Preference | override enable checkbox", "Override language"), &override_lang)) {
ImGui::SetNextItemWidth(-(ImGui::GetFontSize() * (1 + (8 * NEXT_SLIDER_COUNT))) - ((ImGui::GetStyle().ItemSpacing.x + ImGui::GetStyle().FramePadding.x) * (NEXT_SLIDER_COUNT + 1))); if (!override_lang) {
uint8_t components = TimeToComponentCount(playback->GetLength()); lang = DEFAULT_LANG;
string time_str = TimeToString(position, components); SET_LANG(lang.c_str());
if (ImGui::SliderFloat("##Seek", &position, 0.0f, playback->GetLength(), time_str.c_str(), ImGuiSliderFlags_NoRoundToFormat))
playback->Seek(position);
ImGui::SameLine();
if (ImGui::Button(ICON_FK_STOP "##Stop")) {
playback->Stop();
}
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
if (ImGui::SliderFloat("##Volume", &playback->volume, 0.0, 100.0, ICON_FK_VOLUME_UP ": %.0f%%")) {
playback->Update();
}
const float items = 3.0f;
const float between_items = items - 1.0f;
ImGui::PushItemWidth((ImGui::GetWindowWidth() / items) - (ImGui::GetStyle().ItemSpacing.x / (items / between_items)) - ((ImGui::GetStyle().WindowPadding.x / items) * 2.0f));
if (ImGui::SliderFloat("##Speed", &playback->speed, 0.25, 4.0, _TR_CTX("Playback controls > slider", "Speed: %.2fx"), ImGuiSliderFlags_Logarithmic)) {
playback->Update();
}
ImGui::SameLine();
if (ImGui::SliderFloat("##Tempo", &playback->tempo, 0.25, 4.0, _TR_CTX("Playback controls > slider", "Tempo: %.2fx"), ImGuiSliderFlags_Logarithmic)) {
playback->Update();
}
ImGui::SameLine();
if (ImGui::SliderFloat("##Pitch", &playback->pitch, 0.25, 4.0, _TR_CTX("Playback controls > slider", "Pitch: %.2fx"), ImGuiSliderFlags_Logarithmic)) {
playback->Update();
}
ImGui::PopItemWidth();
}
ImGui::End();
if (prefs_window) {
ImGui::SetNextWindowDockID(dockid);
ImGui::Begin(_TRI_CTX(ICON_FK_COG, "Window title, window opened by menu item", "Preferences..."), &prefs_window);
{
if (ImGui::Checkbox(_TR_CTX("Preference > VSync checkbox", "Enable VSync"), &vsync)) {
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
} }
}
if (override_lang) {
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x); ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - (ImGui::GetFontSize()) - ((ImGui::GetStyle().ItemSpacing.x + (ImGui::GetStyle().FramePadding.x * 2.0f))) - (ImGui::GetStyle().WindowPadding.x));
ImGui::SliderInt("##Framerate", &framerate, 10, 480, _TR_CTX("Preferences > Framerate slider", "Max framerate without VSync: %d")); ImGui::InputText("##LanguageOverrideTextBox", &lang);
if (ImGui::Button(_TRI_CTX(ICON_FK_MAGIC, "Preference > Related non-preference button", "Theme Editor"), ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), 0))) { ImGui::SameLine();
theme_editor = true; if (ImGui::Button(ICON_FK_CHECK)) {
SET_LANG(lang.c_str());
} }
static string filter = ""; }
ImGui::Text(_TR_CTX("Preference > Theme selector > Filter label", "Filter:")); ImGui::SameLine(); static string filter = "";
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x); ImGui::Text(_TR_CTX("Preference | Theme selector | Filter label", "Filter:")); ImGui::SameLine();
ImGui::InputText("##FilterInput", &filter); ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x);
ImGui::Text(_TR_CTX("Preferences > Theme selector > Selector label", "Select a theme...")); ImGui::InputText("##FilterInput", &filter);
ImVec2 ChildSize = ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), -ImGui::GetFrameHeightWithSpacing()); ImGui::Text(_TR_CTX("Preferences | Theme selector | Selector label", "Select a theme..."));
if (ImGui::BeginChildFrame(ImGui::GetID("##ThemesContainer"), ChildSize)) { ImVec2 ChildSize = ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), -ImGui::GetFrameHeightWithSpacing());
ImVec2 TableSize = ImVec2(0, 0); if (ImGui::BeginChildFrame(ImGui::GetID("##ThemesContainer"), ChildSize)) {
if (ImGui::BeginTable("##Themes", 2, ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoSavedSettings|ImGuiTableFlags_ScrollY, TableSize)) { ImVec2 TableSize = ImVec2(0, 0);
// Text in TableSetupColumn calls not translated because they're not visible to the user. if (ImGui::BeginTable("##Themes", 2, ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoSavedSettings|ImGuiTableFlags_ScrollY, TableSize)) {
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch); // Text in TableSetupColumn calls not translated because they're not visible to the user.
ImGui::TableSetupColumn("Remove", 0); ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
for (auto themePath : Theme::availableThemes) { ImGui::TableSetupColumn("Remove", 0);
string themeStem = themePath.stem().string(); for (auto themePath : Theme::availableThemes) {
if (themeStem.starts_with(filter)) { string themeStem = themePath.stem().string();
ImGui::TableNextRow(); if (themeStem.starts_with(filter)) {
ImGui::TableSetColumnIndex(0); ImGui::TableNextRow();
const bool is_selected = themePath == theme->file_path; ImGui::TableSetColumnIndex(0);
if (ImGui::Selectable((theme->themeStrings[themePath].name + string(" (") + string(themeStem) + string(")")).c_str(), is_selected, 0)) { const bool is_selected = themePath == theme->file_path;
delete theme; if (ImGui::Selectable((theme->themeStrings[themePath].name + string(" (") + string(themeStem) + string(")")).c_str(), is_selected, 0)) {
theme = new Theme(themePath); delete theme;
theme->Apply(accent_color); theme = new Theme(themePath);
theme->Apply(accent_color);
break;
}
if (is_selected) {
ImGui::SetItemDefaultFocus();
} else {
ImGui::TableSetColumnIndex(1);
if (ImGui::SmallButton((string(ICON_FK_WINDOW_CLOSE "##") + themeStem).c_str())) {
std::filesystem::remove(themePath);
Theme::updateAvailableThemes();
break; break;
} }
if (is_selected) {
ImGui::SetItemDefaultFocus();
} else {
ImGui::TableSetColumnIndex(1);
if (ImGui::SmallButton((string(ICON_FK_WINDOW_CLOSE "##") + themeStem).c_str())) {
std::filesystem::remove(themePath);
Theme::updateAvailableThemes();
break;
}
}
} }
} }
ImGui::EndTable();
} }
} ImGui::EndTable();
ImGui::EndChildFrame();
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2));
if (ImGui::SliderFloat("##AccentColor", &accent_color, 0.0, 360.0, _TR_CTX("Preference > Accent hue slider, range 0-360 from HSV algorithm hue component", "Accent color hue: %.0f°"), ImGuiSliderFlags_NoRoundToFormat)) {
theme->Apply(accent_color);
}
}
ImGui::End();
}
if (about_window) {
ImGui::SetNextWindowDockID(dockid);
if (ImGui::Begin(_TRI_CTX(ICON_FK_INFO, "Window title, window opened by menu item", "About and Licenses"), &about_window)) {
ImGui::PushFont(title);
static const string APP_NAME_STR = _TR_CTX("Application name - Japanese word should remain Japanese and stay the same when translating for stylistic purposes.", "ねこ Player");
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, 0.0f, APP_NAME_STR.c_str()).x) / 2.0f);
ImGui::Text(APP_NAME_STR.c_str());
ImGui::PopFont();
static const string APP_NAME_ROMANIZED = _TR_CTX("Application name - Japanese word should be converted to the translated language's characters. Use an empty string to disable for Japanese.", "(Neko Player)");
if (APP_NAME_ROMANIZED != "") {
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, 0.0f, APP_NAME_ROMANIZED.c_str()).x) / 2.0f);
ImGui::Text(APP_NAME_ROMANIZED.c_str());
}
static const string VER_STRING = _TR_CTX("Version string format specifier", "Version ") + string(TAG) + _TRS_CTX("Suffix to the version string in the about window, if needed", " ");
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, 0.0f, VER_STRING.c_str()).x) / 2.0f);
ImGui::Text(VER_STRING.c_str());
ImGui::NewLine();
static vector<LicenseData> projects = {
LicenseData(APP_NAME_STR, "MIT"),
LicenseData(_TR_CTX("Library name", "SDL Mixer X"), "Zlib"),
LicenseData(_TR_CTX("Library name", "JsonCpp"), "MIT"),
LicenseData(_TR_CTX("Library name", "SoundTouch"), "LGPL-2.1-only"),
LicenseData(_TR_CTX("Library name", "libintl"), "LGPL-2.1-only"),
LicenseData(_TR_CTX("Library name", "Dear ImGui"), "MIT"),
LicenseData(_TR_CTX("Library name", "imgui-filebrowser"), "MIT"),
#ifdef PORTALS
LicenseData(_TR_CTX("Library name", "libportal"), "LGPL-3.0-only"), // Only include the license if it applies.
#endif
LicenseData(_TR_CTX("Library name", "Noto Sans"), "OFL-1.1-RFN"),
LicenseData(_TR_CTX("Library name", "Fork Awesome"), "OFL-1.1-RFN"),
LicenseData(_TR_CTX("Library name", "IconFontCppHeaders"), "Zlib")
};
// Do this in an inner scope so that 'i' isn't accidentally used outside it,
// and so that 'i' can refer to another variable such as in a for loop.
{
int i = 0;
// Use a variable instead of hardcoding so that a #ifdef can change the indices later on.
LOAD_LICENSE(projects[i], nekoplayer); i++;
LOAD_LICENSE(projects[i], sdl_mixer_x); i++;
LOAD_LICENSE(projects[i], jsoncpp); i++;
LOAD_LICENSE(projects[i], soundtouch); i++;
LOAD_LICENSE(projects[i], libintl); i++;
LOAD_LICENSE(projects[i], imgui); i++;
LOAD_LICENSE(projects[i], imgui_filebrowser); i++;
#ifdef PORTALS
LOAD_LICENSE(projects[i], libportal); i++;
#endif
LOAD_LICENSE(projects[i], notosans); i++;
LOAD_LICENSE(projects[i], forkawesome); i++;
LOAD_LICENSE(projects[i], icnfntcpphdrs); i++;
}
// Left
static LicenseData selected = projects[0];
{
ImGui::BeginGroup();
ImGui::TextUnformatted(_TR_CTX("Project selector label.", "Project"));
// Next string is internal.
ImGui::BeginChild("project selector", ImVec2(150, 0), true);
for (auto project : projects)
{
if (ImGui::Selectable(project.Project.c_str(), selected.Project == project.Project))
selected = project;
}
ImGui::EndChild();
ImGui::EndGroup();
}
ImGui::SameLine();
// Right
{
ImGui::BeginGroup();
ImGui::TextUnformatted(_TR_CTX("License viewer label", "License"));
// Next string is internal.
ImGui::BeginChild("license view", ImVec2(0, 0), true); // *don't* leave room for the nonexistant line below us!
ImGui::Text(_TR_CTX("License viewer > information above license - string 1: selected project, string 2: SPDX license identifier", "%s: %s"), selected.Project.c_str(), selected.Spdx.c_str());
ImGui::Separator();
ImGui::TextWrapped("%s", selected.LicenseContents.c_str());
ImGui::EndChild();
ImGui::EndGroup();
} }
} }
ImGui::End(); ImGui::EndChildFrame();
} ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2));
if (theme_editor) { ImGui::ColorEdit4("##AccentColor", &accent_color.x, ImGuiColorEditFlags_InputHSV|ImGuiColorEditFlags_DisplayHSV|ImGuiColorEditFlags_Float);
Theme::ShowEditor(&theme_editor, theme, dockid, window_width, window_height);
theme->Apply(accent_color); theme->Apply(accent_color);
} }
if (fileDialog.IsOpened()) { ImGui::End();
fileDialog.SetWindowSize(window_width, window_height);
fileDialog.SetWindowPos(0, 0);
}
fileDialog.Display();
if (fileDialog.HasSelected()) {
playback->Start(fileDialog.GetSelected().string());
SDL_SetWindowTitle(window, (fileDialog.GetSelected().filename().replace_extension("").string() + std::string(" - ") + std::string(NAME)).c_str());
fileDialog.ClearSelected();
}
if (playback->IsStopped() && !stopped) {
SDL_SetWindowTitle(window, NAME);
}
// Rendering
ImGui::Render();
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
// Update and Render additional Platform Windows
// (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere.
// For this specific demo app we could also call SDL_GL_MakeCurrent(window, gl_context) directly)
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow();
SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext();
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
SDL_GL_MakeCurrent(backup_current_window, backup_current_context);
}
SDL_GL_SwapWindow(window);
if (!vsync) {
std::this_thread::sleep_until(next_frame);
}
} }
// Cleanup if (about_window) {
#ifdef __EMSCRIPTEN__ ImGui::SetNextWindowDockID(dockid);
EMSCRIPTEN_MAINLOOP_END; if (ImGui::Begin(_TRI_CTX(ICON_FK_INFO, "Window title, window opened by menu item", "About and Licenses"), &about_window)) {
#endif ImGui::PushFont(title);
static const string APP_NAME_STR = _TR_CTX("Application name - Japanese word should remain Japanese and stay the same when translating for stylistic purposes.", "ねこ Player");
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, 0.0f, APP_NAME_STR.c_str()).x) / 2.0f);
ImGui::Text(APP_NAME_STR.c_str());
ImGui::PopFont();
static const string APP_NAME_ROMANIZED = _TR_CTX("Application name - Japanese word should be converted to the translated language's characters. Use a string with only a single underscore to disable for Japanese.", "(Neko Player)");
if (APP_NAME_ROMANIZED != "_") {
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, 0.0f, APP_NAME_ROMANIZED.c_str()).x) / 2.0f);
ImGui::Text(APP_NAME_ROMANIZED.c_str());
}
static const string VER_STRING = _TR_CTX("Version string format specifier", "Version ") + string(TAG) + _TRS_CTX("Suffix to the version string in the about window, if needed", " ");
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, 0.0f, VER_STRING.c_str()).x) / 2.0f);
ImGui::Text(VER_STRING.c_str());
ImGui::NewLine();
static vector<LicenseData> projects = {
LicenseData(APP_NAME_STR, "MIT"),
LicenseData(_TR_CTX("Library name", "SDL Mixer X"), "Zlib"),
LicenseData(_TR_CTX("Library name", "CLI11"), "BSD-3-Clause"),
LicenseData(_TR_CTX("Library name", "JsonCpp"), "MIT"),
LicenseData(_TR_CTX("Library name", "SoundTouch"), "LGPL-2.1-only"),
LicenseData(_TR_CTX("Library name", "libintl"), "LGPL-2.1-only"),
LicenseData(_TR_CTX("Library name", "Dear ImGui"), "MIT"),
LicenseData(_TR_CTX("Library name", "imgui-filebrowser"), "MIT"),
#ifdef PORTALS
LicenseData(_TR_CTX("Library name", "libportal"), "LGPL-3.0-only"), // Only include the license if it applies.
#endif
LicenseData(_TR_CTX("Library name", "Noto Sans"), "OFL-1.1-RFN"),
LicenseData(_TR_CTX("Library name", "Fork Awesome"), "OFL-1.1-RFN"),
LicenseData(_TR_CTX("Library name", "IconFontCppHeaders"), "Zlib")
};
// Do this in an inner scope so that 'i' isn't accidentally used outside it,
// and so that 'i' can refer to another variable such as in a for loop.
{
int i = 0;
// Use a variable instead of hardcoding so that a #ifdef can change the indices later on.
LOAD_LICENSE(projects[i], nekoplayer); i++;
LOAD_LICENSE(projects[i], sdl_mixer_x); i++;
LOAD_LICENSE(projects[i], cli11); i++;
LOAD_LICENSE(projects[i], jsoncpp); i++;
LOAD_LICENSE(projects[i], soundtouch); i++;
LOAD_LICENSE(projects[i], libintl); i++;
LOAD_LICENSE(projects[i], imgui); i++;
LOAD_LICENSE(projects[i], imgui_filebrowser); i++;
#ifdef PORTALS
LOAD_LICENSE(projects[i], libportal); i++;
#endif
LOAD_LICENSE(projects[i], notosans); i++;
LOAD_LICENSE(projects[i], forkawesome); i++;
LOAD_LICENSE(projects[i], icnfntcpphdrs); i++;
}
// Left
static LicenseData selected = projects[0];
{
ImGui::BeginGroup();
ImGui::TextUnformatted(_TR_CTX("Project selector label.", "Project"));
// Next string is internal.
ImGui::BeginChild("project selector", ImVec2(150, 0), true);
for (auto project : projects)
{
if (ImGui::Selectable(project.Project.c_str(), selected.Project == project.Project))
selected = project;
}
ImGui::EndChild();
ImGui::EndGroup();
}
ImGui::SameLine();
// Right
{
ImGui::BeginGroup();
ImGui::TextUnformatted(_TR_CTX("License viewer label", "License"));
// Next string is internal.
ImGui::BeginChild("license view", ImVec2(0, 0), true); // *don't* leave room for the nonexistant line below us!
ImGui::Text(_TR_CTX("License viewer | information above license - string 1: selected project, string 2: SPDX license identifier", "%s: %s"), selected.Project.c_str(), selected.Spdx.c_str());
ImGui::Separator();
ImGui::TextWrapped("%s", selected.LicenseContents.c_str());
ImGui::EndChild();
ImGui::EndGroup();
}
}
ImGui::End();
}
// Display the theme editor.
if (theme_editor) {
Theme::ShowEditor(&theme_editor, theme, dockid, window_width, window_height);
// Immediately apply any changes made in the theme editor.
theme->Apply(accent_color);
}
if (fileDialog.IsOpened()) {
// Make the fallback file dialog fill the window.
fileDialog.SetWindowSize(window_width, window_height);
fileDialog.SetWindowPos(0, 0);
}
// Display the file dialog
fileDialog.Display();
// Load a new file when it has been selected.
if (fileDialog.HasSelected()) {
LoadFile(fileDialog.GetSelected().string());
// Make sure to not load the file unnecessarily.
fileDialog.ClearSelected();
}
if (playback->IsStopped() && !stopped) {
// Update the window title to reflect that no file is playing.
SetWindowTitle(NAME);
}
}
void MainLoop::LoadFile(std::string file) {
playback->Start(file);
// Update the window title.
SetWindowTitle((file + std::string(" - ") + std::string(NAME)).c_str());
}
void MainLoop::Deinit() {
delete playback; delete playback;
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_GL_DeleteContext(gl_context);
SDL_DestroyWindow(window);
IMG_Quit();
SDL_Quit();
{ {
Json::Value config; Json::Value config;
std::ofstream stream; std::ofstream stream;
@ -636,13 +430,37 @@ int main(int, char**)
if (!themePath.empty()) { if (!themePath.empty()) {
config["theme_name"] = themePath.filename().string(); config["theme_name"] = themePath.filename().string();
} }
config["accent_color"] = accent_color; {
Json::Value accentColor;
accentColor["h"] = accent_color.x;
accentColor["s"] = accent_color.y;
accentColor["v"] = accent_color.z;
accentColor["a"] = accent_color.w;
config["accent_color"] = accentColor;
}
config["demo_window"] = show_demo_window; config["demo_window"] = show_demo_window;
config["vsync"] = vsync; config["vsync"] = vsync;
config["framerate"] = framerate; config["framerate"] = framerate;
if (lang == DEFAULT_LANG) {
config["lang"] = Json::Value::nullSingleton();
} else {
config["lang"] = lang;
}
stream << config; stream << config;
stream.close(); stream.close();
} }
free((void*)io.IniFilename); }
return 0; MainLoop::MainLoop() : RendererBackend() {
}
// Main code
int main(int argc, char** argv)
{
MainLoop loop;
vector<std::string> args;
for (int i = 1; i < argc; i++) {
args.push_back(std::string(argv[i]));
}
loop.args = args;
return loop.Run();
} }

56
main.h Normal file
View file

@ -0,0 +1,56 @@
#pragma once
#include "RendererBackend.h"
#include "config.h"
#include "file_browser.h"
#include "playback.h"
#include "theme.h"
#include <libintl.h>
#include <iostream>
#include <fstream>
#include <json/json.h>
#include <stdio.h>
#include <numbers>
#include <cmath>
#include <cstdlib>
#include <string>
#include <SDL.h>
#include <SDL_image.h>
#include <filesystem>
#include <SDL_video.h>
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <SDL_opengles2.h>
#else
#include <SDL_opengl.h>
#endif
#include "license.h"
#include "base85.h"
#include "IconsForkAwesome.h"
#include "imgui.h"
#include "imgui_stdlib.h"
#include "translation.h"
#ifdef __EMSCRIPTEN__
#include "../libs/emscripten/emscripten_mainloop_stub.h"
#endif
using namespace std::filesystem;
using namespace std::numbers;
using std::string;
class MainLoop : public RendererBackend {
bool show_demo_window = false;
FileBrowser fileDialog = FileBrowser(false);
std::string userdir;
Playback *playback;
float position = 0.0;
bool prefs_window = false;
bool theme_editor = false;
bool about_window = false;
bool stopped = true;
public:
vector<std::string> args;
void LoadFile(std::string file);
void Init() override;
void GuiFunction() override;
void Deinit() override;
void Drop(std::string file) override;
MainLoop();
};

View file

@ -6,12 +6,13 @@ cmake = import('cmake')
#if get_option('debug') #if get_option('debug')
# add_global_arguments('-DDEBUG', language: 'cpp') # add_global_arguments('-DDEBUG', language: 'cpp')
#endif #endif
add_global_arguments('-DIMGUI_USER_CONFIG="imgui_config.h"', language: 'cpp')
if get_option('gles') or target_machine.cpu_family() == 'aarch64' or target_machine.cpu_family() == 'arm' if get_option('gles') or target_machine.cpu_family() == 'aarch64' or target_machine.cpu_family() == 'arm'
add_global_arguments('-DIMGUI_IMPL_OPENGL_ES2', language: 'cpp') add_global_arguments('-DIMGUI_IMPL_OPENGL_ES2', language: 'cpp')
endif endif
cfg_data = configuration_data() cfg_data = configuration_data()
cfg_data.set('DEBUG', get_option('debug')) cfg_data.set('DEBUG', get_option('debug'))
cfg_data.set('LOCALE_DIR', get_option('localedir')) cfg_data.set('LOCALE_DIR', get_option('prefix') / get_option('localedir'))
# SDL Mixer X # SDL Mixer X
smx_opts = cmake.subproject_options() smx_opts = cmake.subproject_options()
smx_opts.add_cmake_defines({'SDL_MIXER_X_STATIC': true, 'SDL_MIXER_X_SHARED': false, 'USE_MIDI_NATIVE_ALT': false, 'USE_MIDI_NATIVE': false}) smx_opts.add_cmake_defines({'SDL_MIXER_X_STATIC': true, 'SDL_MIXER_X_SHARED': false, 'USE_MIDI_NATIVE_ALT': false, 'USE_MIDI_NATIVE': false})
@ -27,9 +28,17 @@ deps = [
subproject('jsoncpp').get_variable('jsoncpp_dep'), subproject('jsoncpp').get_variable('jsoncpp_dep'),
dependency('soundtouch'), dependency('soundtouch'),
dependency('intl'), dependency('intl'),
dependency('threads'),
smx_subproj.dependency('SDL2_mixer_ext_Static') smx_subproj.dependency('SDL2_mixer_ext_Static')
] ]
if get_option('vgmstream')
vgm_opts = cmake.subproject_options()
vgm_opts.add_cmake_defines({'BUILD_FB2K': false, 'BUILD_CLI': false, 'BUILD_WINAMP': false, 'BUILD_XMPLAY': false, 'BUILD_AUDACIOUS': false, 'BUILD_V123': false, 'BUILD_STATIC': false})
#vgm_opts.set_override_option('c_std', 'c99')
vgm_subproj = cmake.subproject('vgmstream', options: vgm_opts)
deps += vgm_subproj.dependency('libvgmstream')
endif
git = find_program('git', required: false) git = find_program('git', required: false)
tag = 'unknown' tag = 'unknown'
if git.found() if git.found()
@ -54,6 +63,7 @@ endif
srcs = [ srcs = [
'main.cpp', 'main.cpp',
'RendererBackend.cpp',
'playback.cpp', 'playback.cpp',
'theme.cpp', 'theme.cpp',
'file_browser.cpp', 'file_browser.cpp',
@ -90,9 +100,10 @@ if ascli_exe.found()
metainfo_file] metainfo_file]
) )
endif endif
install_data('assets/icon.svg', rename: 'neko-player.svg', install_dir: 'share/icons/hicolor/scalable/apps/') install_data('assets/icon.svg', rename: 'neko-player', install_dir: 'share/icons/hicolor/scalable/apps/')
install_data('assets/neko-player.desktop', install_dir: 'share/applications') install_data('assets/neko-player.desktop', install_dir: 'share/applications')
install_data('assets/com.experimentalcraft.NekoPlayer.metainfo.xml', install_dir: 'share/metainfo') install_data('assets/com.experimentalcraft.NekoPlayer.metainfo.xml', install_dir: 'share/metainfo')
install_subdir('assets/translations/', exclude_files: 'neko_player.pot', strip_directory: true, install_dir: get_option('localedir'))
exe = executable('neko-player', srcs, exe = executable('neko-player', srcs,
dependencies: deps, dependencies: deps,
include_directories: include_dirs, include_directories: include_dirs,

View file

@ -1,2 +1,3 @@
option('gles', type: 'boolean', value: false) option('gles', type: 'boolean', value: false)
option('portals', type: 'boolean', value: true) option('portals', type: 'boolean', value: true)
option('vgmstream', type: 'boolean', value: true)

View file

@ -4,17 +4,39 @@
#include <SDL.h> #include <SDL.h>
#include <exception> #include <exception>
#include <thread> #include <thread>
#include <cmath>
#ifdef __linux__
#include <pthread.h>
#endif
using namespace std::chrono; using namespace std::chrono;
size_t CalculateBufSize(SDL_AudioSpec *obtained, double max_seconds, size_t samples_override = 0) { size_t CalculateBufSize(SDL_AudioSpec *obtained, double seconds, double max_seconds, size_t samples_override = 0) {
return ((((samples_override == 0) ? obtained->samples : samples_override) * max_seconds) + 1) * sizeof(SAMPLETYPE) * obtained->channels; return ((((samples_override == 0) ? obtained->samples : samples_override) * std::min(seconds, max_seconds)) + 1) * sizeof(SAMPLETYPE) * obtained->channels;
} }
void Playback::SDLCallbackInner(Uint8 *stream, int len) { void Playback::SDLCallbackInner(Uint8 *stream, int len) {
while (st->numSamples() <= (uint)len) {
general_mixer(NULL, buf, bufsize);
st->putSamples((SAMPLETYPE*)buf, bufsize / sizeof(SAMPLETYPE) / spec.channels);
}
SDL_memset((void*)stream, 0, len); SDL_memset((void*)stream, 0, len);
st->receiveSamples((SAMPLETYPE*)stream, len / sizeof(SAMPLETYPE) / spec.channels); if (!playback_ready.load()) {
return;
}
size_t i = 0;
size_t max = 0;
size_t unit = sizeof(SAMPLETYPE) * spec.channels;
size_t bytes_per_iter = std::min(((bufsize / unit)) * unit, (size_t)fakespec.size);
while (st->numSamples() <= (uint)len) {
if (general_mixer == nullptr) {
return;
}
general_mixer(NULL, buf + i, bytes_per_iter);
i += bytes_per_iter;
max = i + bytes_per_iter;
if (i + bytes_per_iter >= bufsize) {
st->putSamples((SAMPLETYPE*)buf, i / unit);
max = 0;
i = 0;
SDL_memset((void*)buf, 0, bufsize);
}
}
st->putSamples((SAMPLETYPE*)buf, max / unit);
st->receiveSamples((SAMPLETYPE*)stream, len / unit);
} }
void Playback::SDLCallback(void *userdata, Uint8 *stream, int len) { void Playback::SDLCallback(void *userdata, Uint8 *stream, int len) {
((Playback*)userdata)->SDLCallbackInner(stream, len); ((Playback*)userdata)->SDLCallbackInner(stream, len);
@ -34,82 +56,133 @@ void Playback::Unload(Mix_Music *music) {
Mix_HaltMusicStream(music); Mix_HaltMusicStream(music);
Mix_FreeMusic(music); Mix_FreeMusic(music);
} }
void Playback::UpdateST() {
if (speed > 0.0f) {
st->setRate(speed);
}
if (tempo > 0.0f) {
st->setTempo(tempo);
}
if (pitch > 0.0f) {
st->setPitch(pitch);
}
}
double Playback::GetMaxSeconds() {
return std::max(MaxSpeed * MaxTempo, st->getInputOutputSampleRatio());
}
void Playback::ThreadFunc() { void Playback::ThreadFunc() {
if (!SDL_WasInit(SDL_INIT_AUDIO)) { #ifdef __linux__
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { pthread_setname_np(pthread_self(), "Playback control thread");
printf("Error initializing SDL: '%s'\n", SDL_GetError()); #endif
bool reload = false;
while (running) {
playback_ready.store(false);
if (reload) {
printf("Resuming playback...\n");
}
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
printf("Error initializing SDL: '%s'\n", SDL_GetError());
throw std::exception();
}
}
SDL_AudioSpec obtained;
SDL_AudioSpec desired;
desired.format =
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
AUDIO_S16SYS;
#else
AUDIO_F32SYS;
#endif
desired.freq = 48000;
desired.samples = 1024;
desired.channels = 2;
desired.callback = Playback::SDLCallback;
desired.userdata = this;
st = new SoundTouch();
Mix_Init(MIX_INIT_FLAC|MIX_INIT_MID|MIX_INIT_MOD|MIX_INIT_MP3|MIX_INIT_OGG|MIX_INIT_OPUS|MIX_INIT_WAVPACK);
if ((device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE|SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE)) == 0) {
printf("Error opening audio device: '%s'\n", SDL_GetError());
throw std::exception(); throw std::exception();
} }
} spec = obtained;
SDL_AudioSpec obtained; st->setSampleRate(spec.freq);
SDL_AudioSpec desired; st->setChannels(spec.channels);
desired.format = UpdateST();
#ifdef SOUNDTOUCH_INTEGER_SAMPLES bufsize = 0;
AUDIO_S16SYS; fakespec = spec;
#else double maxSeconds = GetMaxSeconds();
AUDIO_F32SYS; fakespec.size *= maxSeconds;
#endif fakespec.samples *= maxSeconds;
desired.freq = 48000; size_t new_bufsize = CalculateBufSize(&spec, GetMaxSeconds(), MaxSeconds);
desired.samples = 1024; buf = (Uint8*)malloc(new_bufsize);
desired.channels = 2; if (buf == NULL) {
desired.callback = Playback::SDLCallback; throw std::exception();
desired.userdata = this;
st = new SoundTouch();
st->setSampleRate(desired.freq);
st->setChannels(desired.channels);
Mix_Init(MIX_INIT_FLAC|MIX_INIT_MID|MIX_INIT_MOD|MIX_INIT_MP3|MIX_INIT_OGG|MIX_INIT_OPUS|MIX_INIT_WAVPACK);
if ((device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, 0)) == 0) {
printf("Error opening audio device: '%s'\n", SDL_GetError());
throw std::exception();
}
spec = obtained;
bufsize = spec.size;
buf = (Uint8*)malloc(bufsize);
general_mixer = Mix_GetGeneralMixer();
Mix_InitMixer(&spec, SDL_TRUE);
SDL_PauseAudioDevice(device, 0);
Mix_Music *music = Load(filePath.c_str());
while (running) {
if (file_changed.exchange(false)) {
Unload(music);
music = Load(filePath.c_str());
} }
if (flag_mutex.try_lock()) { bufsize = new_bufsize;
if (seeking.exchange(false)) { general_mixer = Mix_GetGeneralMixer();
Mix_SetMusicPositionStream(music, position); Mix_InitMixer(&fakespec, SDL_TRUE);
} SDL_PauseAudioDevice(device, 0);
if (paused) { Mix_Music *music = Load(filePath.c_str());
Mix_PauseMusicStream(music); if (reload) {
} else { Seek(position);
Mix_ResumeMusicStream(music);
}
if (update.exchange(false)) {
Mix_VolumeMusicStream(music, (volume / 100.0 * MIX_MAX_VOLUME));
SDL_LockAudioDevice(device);
if (speed > 0.0f) {
st->setRate(speed);
}
if (tempo > 0.0f) {
st->setTempo(tempo);
}
if (pitch > 0.0f) {
st->setPitch(pitch);
}
SDL_UnlockAudioDevice(device);
}
flag_mutex.unlock();
} }
position = Mix_GetMusicPosition(music); reload = false;
std::this_thread::sleep_for(20ms); playback_ready.store(true);
while (running && !reload) {
if (file_changed.exchange(false)) {
Unload(music);
music = Load(filePath.c_str());
}
if (flag_mutex.try_lock()) {
if (seeking.exchange(false)) {
Mix_SetMusicPositionStream(music, position);
}
if (paused) {
Mix_PauseMusicStream(music);
} else {
Mix_ResumeMusicStream(music);
}
if (update.exchange(false)) {
Mix_VolumeMusicStream(music, (volume / 100.0 * MIX_MAX_VOLUME));
SDL_LockAudioDevice(device);
UpdateST();
size_t correct_buf_size = CalculateBufSize(&spec, GetMaxSeconds(), MaxSeconds);
size_t max_buf_size = correct_buf_size * 10;
bool too_large = max_buf_size < bufsize;
bool too_small = correct_buf_size > bufsize;
if (too_large) {
printf("Bufsize is too large - ");
} else if (too_small) {
printf("Bufsize is too small - ");
}
if (too_large || too_small) {
printf("Resizing buffer...\n");
general_mixer = nullptr;
bufsize = 0;
buf = (Uint8*)realloc((void*)buf, correct_buf_size);
if (buf == NULL) {
break;
}
bufsize = correct_buf_size;
}
SDL_UnlockAudioDevice(device);
}
flag_mutex.unlock();
}
position = Mix_GetMusicPosition(music);
std::this_thread::sleep_for(20ms);
}
playback_ready.store(false);
// ====
Unload(music);
SDL_CloseAudioDevice(device);
Mix_CloseAudio();
Mix_Quit();
SDL_QuitSubSystem(SDL_INIT_AUDIO);
delete st;
free(buf);
} }
// ====
Unload(music);
Mix_CloseAudio();
Mix_Quit();
SDL_CloseAudioDevice(device);
delete st;
free(buf);
} }
Playback::Playback() { Playback::Playback() {
@ -131,6 +204,8 @@ void Playback::Start(std::string filePath) {
this->filePath = filePath; this->filePath = filePath;
printf("Playing %s...\n", filePath.c_str()); printf("Playing %s...\n", filePath.c_str());
flag_mutex.lock(); flag_mutex.lock();
this->position = 0.0;
seeking.store(true);
paused = false; paused = false;
Update(); Update();
if (running.exchange(true)) { if (running.exchange(true)) {
@ -169,7 +244,6 @@ void Playback::Stop() {
thread.join(); thread.join();
} }
} }
void Playback::Update() { void Playback::Update() {
update.store(true); update.store(true);
} }

View file

@ -9,8 +9,10 @@
#include <mutex> #include <mutex>
#include <SoundTouch.h> #include <SoundTouch.h>
#include <span> #include <span>
#include <optional>
using namespace soundtouch; using namespace soundtouch;
using std::span; using std::span;
using std::optional;
class Playback { class Playback {
private: private:
std::string filePath; std::string filePath;
@ -18,6 +20,8 @@ private:
std::atomic_bool file_changed; std::atomic_bool file_changed;
std::atomic_bool seeking; std::atomic_bool seeking;
std::atomic_bool update; std::atomic_bool update;
std::atomic_bool restart;
std::atomic_bool playback_ready;
std::mutex flag_mutex; std::mutex flag_mutex;
std::thread thread; std::thread thread;
double position; double position;
@ -29,11 +33,15 @@ private:
SDL_AudioDeviceID device; SDL_AudioDeviceID device;
SoundTouch *st; SoundTouch *st;
SDL_AudioSpec spec; SDL_AudioSpec spec;
/// @brief A fake SDL_AudioSpec used to trick SDL Mixer X into allocating a bigger buffer.
SDL_AudioSpec fakespec;
void SDLCallbackInner(Uint8 *stream, int len); void SDLCallbackInner(Uint8 *stream, int len);
static void SDLCallback(void *userdata, Uint8 *stream, int len); static void SDLCallback(void *userdata, Uint8 *stream, int len);
Mix_Music *Load(const char* file); Mix_Music *Load(const char* file);
void Unload(Mix_Music* music); void Unload(Mix_Music* music);
void ThreadFunc(); void ThreadFunc();
void UpdateST();
double GetMaxSeconds();
public: public:
Playback(); Playback();
~Playback(); ~Playback();
@ -50,4 +58,11 @@ public:
float speed; float speed;
float tempo; float tempo;
float pitch; float pitch;
double MaxSeconds = 100.0;
double MaxSpeed = 4.0;
double MaxPitch = 4.0;
double MaxTempo = 4.0;
double MinSpeed = 0.25;
double MinPitch = 0.25;
double MinTempo = 0.25;
}; };

1
subprojects/vgmstream Submodule

@ -0,0 +1 @@
Subproject commit 416ac26510b4e243760d9c3a1fdb81a885762a8b

200
theme.cpp
View file

@ -40,7 +40,7 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
ImGuiStyle& style = theme->style; ImGuiStyle& style = theme->style;
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f); ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f);
ImVec2 buttonSize = ImVec2((ImGui::GetWindowWidth() * 0.50f) - (ImGui::GetStyle().WindowPadding.x) - (ImGui::GetStyle().ItemSpacing.x * 0.5f), 0); ImVec2 buttonSize = ImVec2((ImGui::GetWindowWidth() * 0.50f) - (ImGui::GetStyle().WindowPadding.x) - (ImGui::GetStyle().ItemSpacing.x * 0.5f), 0);
if (ImGui::Button(_TR_CTX("Theme Editor > preset button", "Create light"), buttonSize)) { if (ImGui::Button(_TR_CTX("Theme Editor | preset button", "Create light"), buttonSize)) {
delete theme; delete theme;
theme = new Theme(false); theme = new Theme(false);
@ -49,7 +49,7 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
return true; return true;
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(_TR_CTX("Theme Editor > preset button", "Create dark"), buttonSize)) { if (ImGui::Button(_TR_CTX("Theme Editor | preset button", "Create dark"), buttonSize)) {
delete theme; delete theme;
theme = new Theme(true); theme = new Theme(true);
@ -57,7 +57,7 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
ImGui::End(); ImGui::End();
return true; return true;
} }
if (ImGui::Button(_TR_CTX("Theme Editor > import button. Opens the theme import file dialog", "Import..."), buttonSize)) { if (ImGui::Button(_TR_CTX("Theme Editor | import button. Opens the theme import file dialog", "Import..."), buttonSize)) {
importDialog.SetTitle(_TR_CTX("Theme Editor file dialog title", "Import theme...")); importDialog.SetTitle(_TR_CTX("Theme Editor file dialog title", "Import theme..."));
importDialog.SetTypeFilters(_TR_CTX("Theme Editor file dialog filter name", "Theme JSON files"), { ".json"}); importDialog.SetTypeFilters(_TR_CTX("Theme Editor file dialog filter name", "Theme JSON files"), { ".json"});
std::string userdir = std::getenv( std::string userdir = std::getenv(
@ -71,7 +71,7 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
importDialog.Open(); importDialog.Open();
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(_TR_CTX("Theme Editor > export button. Opens the theme export file dialog", "Export..."), buttonSize)) { if (ImGui::Button(_TR_CTX("Theme Editor | export button. Opens the theme export file dialog", "Export..."), buttonSize)) {
exportDialog.SetTitle(_TR_CTX("Theme Editor file dialog title", "Export theme...")); exportDialog.SetTitle(_TR_CTX("Theme Editor file dialog title", "Export theme..."));
exportDialog.SetTypeFilters(_TR_CTX("Theme Editor file dialog filter name", "Theme JSON files"), { ".json"}); exportDialog.SetTypeFilters(_TR_CTX("Theme Editor file dialog filter name", "Theme JSON files"), { ".json"});
std::string userdir = std::getenv( std::string userdir = std::getenv(
@ -95,7 +95,7 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
importDialog.Display(); importDialog.Display();
exportDialog.Display(); exportDialog.Display();
if (!theme->file_path.empty()) { if (!theme->file_path.empty()) {
if (ImGui::Button(_TR_CTX("Theme Editor > button. Reverts to saved file.", "Revert"), buttonSize)) { if (ImGui::Button(_TR_CTX("Theme Editor | button. Reverts to saved file.", "Revert"), buttonSize)) {
string file_path_backup = theme->file_path; string file_path_backup = theme->file_path;
delete theme; delete theme;
theme = new Theme(file_path_backup); theme = new Theme(file_path_backup);
@ -104,82 +104,82 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
return true; return true;
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(_TR_CTX("Theme Editor > button. Saves the theme to it's current location. Not shown when it doesn't already have one.", "Save"), buttonSize)) { if (ImGui::Button(_TR_CTX("Theme Editor | button. Saves the theme to it's current location. Not shown when it doesn't already have one.", "Save"), buttonSize)) {
theme->Save(theme->file_path); theme->Save(theme->file_path);
} }
} }
if (ImGui::Button(_TR_CTX("Theme Editor > button. Opens the theme loading dialog for themes created or imported by the user.", "Load..."), buttonSize)) { if (ImGui::Button(_TR_CTX("Theme Editor | button. Opens the theme loading dialog for themes created or imported by the user.", "Load..."), buttonSize)) {
loadOpen = true; loadOpen = true;
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(_TR_CTX("Theme Editor > button. Opens the theme saving dialog for themes created or imported by the user.", "Save as..."), buttonSize)) { if (ImGui::Button(_TR_CTX("Theme Editor | button. Opens the theme saving dialog for themes created or imported by the user.", "Save as..."), buttonSize)) {
saveAsOpen = true; saveAsOpen = true;
} }
// Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f) // Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)
if (ImGui::SliderFloat(_TR_CTX("Theme Editor > slider. Simplified frame rounding.", "Frame Rounding"), &style.FrameRounding, 0.0f, 12.0f, "%.0f")) if (ImGui::SliderFloat(_TR_CTX("Theme Editor | slider. Simplified frame rounding.", "Frame Rounding"), &style.FrameRounding, 0.0f, 12.0f, "%.0f"))
style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding
{ bool border = (style.WindowBorderSize > 0.0f); if (ImGui::Checkbox(_TR_CTX("Theme Editor > checkbox", "Window Border"), &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } } { bool border = (style.WindowBorderSize > 0.0f); if (ImGui::Checkbox(_TR_CTX("Theme Editor | checkbox", "Window Border"), &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } }
ImGui::SameLine(); ImGui::SameLine();
{ bool border = (style.FrameBorderSize > 0.0f); if (ImGui::Checkbox(_TR_CTX("Theme Editor > checkbox", "Frame Border"), &border)) { style.FrameBorderSize = border ? 1.0f : 0.0f; } } { bool border = (style.FrameBorderSize > 0.0f); if (ImGui::Checkbox(_TR_CTX("Theme Editor | checkbox", "Frame Border"), &border)) { style.FrameBorderSize = border ? 1.0f : 0.0f; } }
ImGui::SameLine(); ImGui::SameLine();
{ bool border = (style.PopupBorderSize > 0.0f); if (ImGui::Checkbox(_TR_CTX("Theme Editor > checkbox", "Popup Border"), &border)) { style.PopupBorderSize = border ? 1.0f : 0.0f; } } { bool border = (style.PopupBorderSize > 0.0f); if (ImGui::Checkbox(_TR_CTX("Theme Editor | checkbox", "Popup Border"), &border)) { style.PopupBorderSize = border ? 1.0f : 0.0f; } }
ImGui::Separator(); ImGui::Separator();
if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None)) if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None))
{ {
if (ImGui::BeginTabItem(_TR_CTX("Theme Editor > Tab label", "Strings"))) if (ImGui::BeginTabItem(_TR_CTX("Theme Editor | Tab label", "Strings")))
{ {
ImGui::InputText(_TR_CTX("Theme Editor > Strings > Name input label", "Name"), &theme->strings["fallback"].name); ImGui::InputText(_TR_CTX("Theme Editor | Strings | Name input label", "Name"), &theme->strings["fallback"].name);
ImGui::InputText(_TR_CTX("Theme Editor > Strings > Description input label", "Description"), &theme->strings["fallback"].description); ImGui::InputText(_TR_CTX("Theme Editor | Strings | Description input label", "Description"), &theme->strings["fallback"].description);
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (ImGui::BeginTabItem(_TR_CTX("Theme Editor > Tab label", "Sizes"))) if (ImGui::BeginTabItem(_TR_CTX("Theme Editor | Tab label", "Sizes")))
{ {
ImGui::SeparatorText(_TR_CTX("Theme Editor > Sizes > Section label", "Sizing")); ImGui::SeparatorText(_TR_CTX("Theme Editor | Sizes | Section label", "Sizing"));
ImGui::SliderFloat2(_TR_CTX("Theme Editor > Sizes > Sizing > label of XY sliders", "Window Padding"), (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "Window Padding"), (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2(_TR_CTX("Theme Editor > Sizes > Sizing > label of XY sliders", "Frame Padding"), (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "Frame Padding"), (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2(_TR_CTX("Theme Editor > Sizes > Sizing > label of XY sliders", "Cell Padding"), (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "Cell Padding"), (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2(_TR_CTX("Theme Editor > Sizes > Sizing > label of XY sliders", "Item Spacing"), (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "Item Spacing"), (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2(_TR_CTX("Theme Editor > Sizes > Sizing > label of XY sliders", "Inner Item Spacing"), (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "Inner Item Spacing"), (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f");
//ImGui::SliderFloat2(_TR_CTX("Theme Editor > Sizes > Sizing > label of XY sliders", "TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); //ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f");
//ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Sizing > slider label", "IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f"); //ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Sizing | slider label", "IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Sizing > slider label", "Scrollbar Size"), &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Sizing | slider label", "Scrollbar Size"), &style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Sizing > slider label", "Minimum Grabber Size"), &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Sizing | slider label", "Minimum Grabber Size"), &style.GrabMinSize, 1.0f, 20.0f, "%.0f");
ImGui::SliderFloat2(_TR_CTX("Theme Editor > Sizes > Sizing > label of XY sliders", "Separator Text Padding"), (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f"); ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "Separator Text Padding"), (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f");
ImGui::SeparatorText(_TR_CTX("Theme Editor > Sizes > Section label", "Borders")); ImGui::SeparatorText(_TR_CTX("Theme Editor | Sizes | Section label", "Borders"));
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Borders > slider label", "Window Border Size"), &style.WindowBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Borders | slider label", "Window Border Size"), &style.WindowBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Borders > slider label", "Child Border Size"), &style.ChildBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Borders | slider label", "Child Border Size"), &style.ChildBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Borders > slider label", "Popup Border Size"), &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Borders | slider label", "Popup Border Size"), &style.PopupBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Borders > slider label", "Frame Border Size"), &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Borders | slider label", "Frame Border Size"), &style.FrameBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Borders > slider label", "Tab Border Size"), &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Borders | slider label", "Tab Border Size"), &style.TabBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Borders > slider label", "Separator Text Border Size"), &style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Borders | slider label", "Separator Text Border Size"), &style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f");
ImGui::SeparatorText(_TR_CTX("Theme Editor > Sizes > Section label", "Rounding")); ImGui::SeparatorText(_TR_CTX("Theme Editor | Sizes | Section label", "Rounding"));
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Rounding > slider label", "Window Rounding"), &style.WindowRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Window Rounding"), &style.WindowRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Rounding > slider label", "Child Rounding"), &style.ChildRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Child Rounding"), &style.ChildRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Rounding > slider label", "Frame Rounding"), &style.FrameRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Frame Rounding"), &style.FrameRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Rounding > slider label", "Popup Rounding"), &style.PopupRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Popup Rounding"), &style.PopupRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Rounding > slider label", "Scrollbar Rounding"), &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Scrollbar Rounding"), &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Rounding > slider label", "Grabber Rounding"), &style.GrabRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Grabber Rounding"), &style.GrabRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor > Sizes > Rounding > slider label", "Tab Rounding"), &style.TabRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Tab Rounding"), &style.TabRounding, 0.0f, 12.0f, "%.0f");
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (ImGui::BeginTabItem(_TR_CTX("Theme Editor > Tab label", "Colors"))) if (ImGui::BeginTabItem(_TR_CTX("Theme Editor | Tab label", "Colors")))
{ {
static ImGuiTextFilter filter; static ImGuiTextFilter filter;
filter.Draw(_TR_CTX("Theme Editor > Colors > text filter", "Filter colors"), ImGui::GetFontSize() * 16); filter.Draw(_TR_CTX("Theme Editor | Colors | text filter", "Filter colors"), ImGui::GetFontSize() * 16);
static ImGuiColorEditFlags alpha_flags = 0; static ImGuiColorEditFlags alpha_flags = 0;
if (ImGui::RadioButton(_TR_CTX("Theme Editor > Colors > preview settings radio button", "Opaque"), alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); if (ImGui::RadioButton(_TR_CTX("Theme Editor | Colors | preview settings radio button", "Opaque"), alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine();
if (ImGui::RadioButton(_TR_CTX("Theme Editor > Colors > preview settings radio button", "Alpha"), alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine(); if (ImGui::RadioButton(_TR_CTX("Theme Editor | Colors | preview settings radio button", "Alpha"), alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine();
if (ImGui::RadioButton(_TR_CTX("Theme Editor > Colors > preview settings radio button", "Both"), alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } if (ImGui::RadioButton(_TR_CTX("Theme Editor | Colors | preview settings radio button", "Both"), alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; }
ImGui::BeginChild("##colors", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened); ImGui::BeginChild("##colors", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened);
ImGui::PushItemWidth(-160); ImGui::PushItemWidth(-160);
@ -189,15 +189,12 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
if (!filter.PassFilter(name)) if (!filter.PassFilter(name))
continue; continue;
ImGui::PushID(i); ImGui::PushID(i);
bool hueEnabled = theme->HueEnabledColors.contains(i); AccentColorizer &colorizer = theme->AccentColorizers[i];
if (ImGui::Checkbox(_TR_CTX("Theme Editor > Colors > (Any color) > recoloring checkbox", "Match hue preference"), &hueEnabled)) { ImGui::Text(_TR_CTX("Theme Editor | Colors | (Any color) | recoloring label", "Match accent color preference: "));
if (hueEnabled) { ImGui::Checkbox(_TR_CTX("Theme Editor | Colors | (Any color) | recoloring checkbox | Hue", "H: "), &colorizer.Hue); ImGui::SameLine();
theme->HueEnabledColors.insert(i); ImGui::Checkbox(_TR_CTX("Theme Editor | Colors | (Any color) | recoloring checkbox | Saturation", "S: "), &colorizer.Saturation); ImGui::SameLine();
} else { ImGui::Checkbox(_TR_CTX("Theme Editor | Colors | (Any color) | recoloring checkbox | Value", "V: "), &colorizer.Value); ImGui::SameLine();
theme->HueEnabledColors.erase(i); ImGui::Checkbox(_TR_CTX("Theme Editor | Colors | (Any color) | recoloring checkbox | Alpha (opacity)", "A: "), &colorizer.Alpha); ImGui::SameLine();
}
}
ImGui::SameLine();
ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags | ImGuiColorEditFlags_DisplayHSV); ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags | ImGuiColorEditFlags_DisplayHSV);
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
ImGui::TextUnformatted(name); ImGui::TextUnformatted(name);
@ -227,16 +224,16 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
exportDialog.ClearSelected(); exportDialog.ClearSelected();
} }
if (loadOpen) { if (loadOpen) {
ImGui::OpenPopup(_TR_CTX("Theme Editor > Custom modal dialog title", "Load...")); ImGui::OpenPopup(_TR_CTX("Theme Editor | Custom modal dialog title", "Load..."));
ImGui::SetNextWindowPos(ImVec2(0, 0)); ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(window_width, window_height)); ImGui::SetNextWindowSize(ImVec2(window_width, window_height));
if (ImGui::BeginPopupModal(_TR_CTX("Theme Editor > Custom modal dialog title", "Load..."), nullptr, ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize)) { if (ImGui::BeginPopupModal(_TR_CTX("Theme Editor | Custom modal dialog title", "Load..."), nullptr, ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize)) {
static path selectedThemePath; static path selectedThemePath;
static string filter = ""; static string filter = "";
ImGui::Text(_TR_CTX("Theme Editor > Load dialog > Theme selector > filter label", "Filter:")); ImGui::SameLine(); ImGui::Text(_TR_CTX("Theme Editor | Load dialog | Theme selector | filter label", "Filter:")); ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x); ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x);
ImGui::InputText("##FilterInput", &filter); ImGui::InputText("##FilterInput", &filter);
ImGui::Text(_TR_CTX("Theme Editor > Load dialog > Theme selector > label", "Available themes...")); ImGui::Text(_TR_CTX("Theme Editor | Load dialog | Theme selector | label", "Available themes..."));
if (ImGui::BeginListBox("##Themes", ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), -ImGui::GetTextLineHeightWithSpacing() - ImGui::GetStyle().WindowPadding.y))) { if (ImGui::BeginListBox("##Themes", ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), -ImGui::GetTextLineHeightWithSpacing() - ImGui::GetStyle().WindowPadding.y))) {
for (auto themePath : Theme::availableThemes) { for (auto themePath : Theme::availableThemes) {
string themeStem = themePath.stem().string(); string themeStem = themePath.stem().string();
@ -252,7 +249,7 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
} }
ImGui::EndListBox(); ImGui::EndListBox();
} }
if (ImGui::Button(_TR_CTX("Theme Editor > Load dialog > Load button", "Load"))) { if (ImGui::Button(_TR_CTX("Theme Editor | Load dialog | Load button", "Load"))) {
if (!selectedThemePath.empty()) { if (!selectedThemePath.empty()) {
filter = ""; filter = "";
loadOpen = false; loadOpen = false;
@ -264,7 +261,7 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
} }
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(_TR_CTX("Theme Editor > Load dialog > Cancel button", "Cancel"))) { if (ImGui::Button(_TR_CTX("Theme Editor | Load dialog | Cancel button", "Cancel"))) {
selectedThemePath = path(); selectedThemePath = path();
filter = ""; filter = "";
loadOpen = false; loadOpen = false;
@ -273,16 +270,16 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
} }
} }
if (saveAsOpen) { if (saveAsOpen) {
ImGui::OpenPopup(_TR_CTX("Theme Editor > Custom modal dialog title", "Save as...")); ImGui::OpenPopup(_TR_CTX("Theme Editor | Custom modal dialog title", "Save as..."));
ImGui::SetNextWindowPos(ImVec2(0, 0)); ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(window_width, window_height)); ImGui::SetNextWindowSize(ImVec2(window_width, window_height));
if (ImGui::BeginPopupModal(_TR_CTX("Theme Editor > Custom modal dialog title", "Save as..."), nullptr, ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize)) { if (ImGui::BeginPopupModal(_TR_CTX("Theme Editor | Custom modal dialog title", "Save as..."), nullptr, ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize)) {
static string selectedThemeName = ""; static string selectedThemeName = "";
static string filter = ""; static string filter = "";
ImGui::Text(_TR_CTX("Theme Editor > Save as dialog > Theme selector > filter label", "Filter:")); ImGui::SameLine(); ImGui::Text(_TR_CTX("Theme Editor | Save as dialog | Theme selector | filter label", "Filter:")); ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x); ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x);
ImGui::InputText("##FilterInput", &filter, 1024); ImGui::InputText("##FilterInput", &filter, 1024);
ImGui::Text(_TR_CTX("Theme Editor > Save as dialog > Theme selector > label", "Available themes...")); ImGui::Text(_TR_CTX("Theme Editor | Save as dialog | Theme selector | label", "Available themes..."));
if (ImGui::BeginListBox("##Themes", ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), -ImGui::GetFrameHeightWithSpacing() - ImGui::GetTextLineHeightWithSpacing() - ImGui::GetStyle().WindowPadding.y))) { if (ImGui::BeginListBox("##Themes", ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), -ImGui::GetFrameHeightWithSpacing() - ImGui::GetTextLineHeightWithSpacing() - ImGui::GetStyle().WindowPadding.y))) {
for (auto themePath : Theme::availableThemes) { for (auto themePath : Theme::availableThemes) {
string themeStem = themePath.stem().string(); string themeStem = themePath.stem().string();
@ -299,8 +296,8 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
ImGui::EndListBox(); ImGui::EndListBox();
} }
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f)); ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f));
ImGui::InputText(_TR_CTX("Theme Editor > Save as dialog > Theme name input label", "Theme name: "), &selectedThemeName); ImGui::InputText(_TR_CTX("Theme Editor | Save as dialog | Theme name input label", "Theme name: "), &selectedThemeName);
if (ImGui::Button(_TR_CTX("Theme Editor > Save as dialog > Save button", "Save"))) { if (ImGui::Button(_TR_CTX("Theme Editor | Save as dialog | Save button", "Save"))) {
path selectedThemePath(selectedThemeName); path selectedThemePath(selectedThemeName);
if (!selectedThemePath.empty() && !selectedThemePath.is_absolute()) { if (!selectedThemePath.empty() && !selectedThemePath.is_absolute()) {
selectedThemeName = ""; selectedThemeName = "";
@ -311,7 +308,7 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
} }
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(_TR_CTX("Theme Editor > Save as dialog > Cancel button", "Cancel"))) { if (ImGui::Button(_TR_CTX("Theme Editor | Save as dialog | Cancel button", "Cancel"))) {
selectedThemeName = ""; selectedThemeName = "";
filter = ""; filter = "";
saveAsOpen = false; saveAsOpen = false;
@ -321,14 +318,52 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
} }
return false; return false;
} }
void Theme::Apply(float hue) { AccentColorizer::AccentColorizer() {
Hue = false;
Saturation = false;
Value = false;
Alpha = false;
}
AccentColorizer::AccentColorizer(Json::Value json) : AccentColorizer() {
if (json.isBool()) {
Hue = json.asBool();
Saturation = Hue;
Value = Hue;
Alpha = Hue;
} else {
Hue = json["hue"].asBool();
Saturation = json["saturation"].asBool();
Value = json["value"].asBool();
Alpha = json["alpha"].asBool();
}
}
Json::Value AccentColorizer::Serialize() {
Json::Value output;
output["hue"] = Hue;
output["saturation"] = Saturation;
output["value"] = Value;
output["alpha"] = Alpha;
return output;
}
void AccentColorizer::Colorize(ImVec4 accent, ImVec4 &color) {
ImVec4 hsv = color;
ImGui::ColorConvertRGBtoHSV(color.x, color.y, color.z, hsv.x, hsv.y, hsv.z);
if (Saturation)
hsv.y = accent.y;
if (Value)
hsv.z = accent.z;
if (Hue)
hsv.x = accent.x;
ImGui::ColorConvertHSVtoRGB(hsv.x, hsv.y, hsv.z, color.x, color.y, color.z);
if (Alpha)
color.w *= accent.w;
}
void Theme::Apply(ImVec4 accent) {
ImGuiStyle& actual_style = ImGui::GetStyle(); ImGuiStyle& actual_style = ImGui::GetStyle();
actual_style = style; actual_style = style;
for (int i = 0; i < ImGuiCol_COUNT; i++) for (int i = 0; i < ImGuiCol_COUNT; i++)
{ {
if (HueEnabledColors.contains(i)) { AccentColorizers[i].Colorize(accent, actual_style.Colors[i]);
actual_style.Colors[i] = change_accent_color(style.Colors[i], hue);
}
} }
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
@ -345,7 +380,7 @@ void Theme::Save(string path) {
stream.open(path); stream.open(path);
{ {
Json::Value metadata; Json::Value metadata;
metadata["SchemaVersion"] = 1; metadata["SchemaVersion"] = 2;
{ {
Json::Value stringsList; Json::Value stringsList;
for (auto kv : strings) { for (auto kv : strings) {
@ -409,7 +444,7 @@ void Theme::Save(string path) {
colorValue["g"] = color.y; colorValue["g"] = color.y;
colorValue["b"] = color.z; colorValue["b"] = color.z;
colorValue["a"] = color.w; colorValue["a"] = color.w;
colorValue["ConvertToAccent"] = HueEnabledColors.contains(i); colorValue["ConvertToAccent"] = AccentColorizers[i].Serialize();
colors[name] = colorValue; colors[name] = colorValue;
} }
config["colors"] = colors; config["colors"] = colors;
@ -461,17 +496,21 @@ Theme::Theme(bool dark) : Theme() {
for (int i = 0; i < ImGuiCol_COUNT; i++) for (int i = 0; i < ImGuiCol_COUNT; i++)
{ {
ImVec4 color = style.Colors[i]; ImVec4 color = style.Colors[i];
auto colorizer = AccentColorizer();
if (color.x != color.y || color.y != color.z) { if (color.x != color.y || color.y != color.z) {
HueEnabledColors.insert(i); colorizer.Hue = true;
colorizer.Saturation = true;
colorizer.Alpha = true;
} }
AccentColorizers[i] = colorizer;
} }
style.FrameRounding = 12; style.FrameRounding = 12;
style.GrabRounding = 12; style.GrabRounding = 12;
style.WindowRounding = 12; style.WindowRounding = 12;
} }
ThemeStrings::ThemeStrings() { ThemeStrings::ThemeStrings() {
name = _TRS_CTX("Theme default strings > name", "A theme"); name = _TRS_CTX("Theme default strings | name", "A theme");
description = _TRS_CTX("Theme default strings > description", "(No description)"); description = _TRS_CTX("Theme default strings | description", "(No description)");
} }
ThemeStrings Theme::GetStrings() { ThemeStrings Theme::GetStrings() {
char *language_c = CURRENT_LANGUAGE; char *language_c = CURRENT_LANGUAGE;
@ -581,11 +620,8 @@ Theme::Theme(string path) : Theme() {
if (colors.isMember(name)) { if (colors.isMember(name)) {
Json::Value colorValue = colors[name]; Json::Value colorValue = colors[name];
ImVec4 color = ImVec4(colorValue["r"].asFloat(), colorValue["g"].asFloat(), colorValue["b"].asFloat(), colorValue["a"].asFloat()); ImVec4 color = ImVec4(colorValue["r"].asFloat(), colorValue["g"].asFloat(), colorValue["b"].asFloat(), colorValue["a"].asFloat());
bool hueShifted = colorValue["ConvertToAccent"].asBool(); AccentColorizers[i] = AccentColorizer(colorValue["ConvertToAccent"]);
style.Colors[i] = color; style.Colors[i] = color;
if (hueShifted) {
HueEnabledColors.insert(i);
}
} }
} }
} }

25
theme.h
View file

@ -17,6 +17,27 @@ struct ThemeStrings {
ThemeStrings(Json::Value config); ThemeStrings(Json::Value config);
}; };
struct AccentColorizer {
/// @brief Whether or not to change the hue.
bool Hue;
/// @brief Whether or not to change the saturation.
bool Saturation;
/// @brief Whether or not to change the value.
bool Value;
/// @brief Whether or not to multiply the alpha.
bool Alpha;
/// @brief Colorizes a color stored as an ImVec4 according to preferences.
void Colorize(ImVec4 accent, ImVec4 &color);
/// @brief Serialize the settings to json.
/// @returns The serialized JSON, as a Json::Value.
Json::Value Serialize();
/// @brief Create a default accent colorizer
AccentColorizer();
/// @brief Deserialize the settings from JSON and construct.
/// @param json The JSON to deserialize from.
AccentColorizer(Json::Value json);
};
class Theme { class Theme {
ImGuiStyle style; ImGuiStyle style;
@ -30,10 +51,10 @@ class Theme {
static const int MaxSchemaVersion = 1; static const int MaxSchemaVersion = 1;
string file_path; string file_path;
std::map<string, ThemeStrings> strings; std::map<string, ThemeStrings> strings;
std::set<int> HueEnabledColors; std::map<int, AccentColorizer> AccentColorizers;
ThemeStrings GetStrings(); ThemeStrings GetStrings();
static bool ShowEditor(bool *open, Theme* &theme, ImGuiID dockid, int window_width, int window_height); static bool ShowEditor(bool *open, Theme* &theme, ImGuiID dockid, int window_width, int window_height);
void Apply(float hue); void Apply(ImVec4 accent);
void Save(string path); void Save(string path);
void Save(path path); void Save(path path);
Theme(); Theme();

9686
thirdparty/CLI11.hpp vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -2,11 +2,11 @@
#include <libintl.h> #include <libintl.h>
#include <string> #include <string>
#include <vector> #include <vector>
// Based on the code found in gettext.h, but without any additionall things we don't need. // Based on the code found in gettext.h, but without any additional things we don't need.
inline static const char *gettext_ctx(const char *ctx, const char *msgid) { inline static const char *gettext_ctx(const char *ctx, const char *msgid) {
const char *msg_ctxt_id = (std::string(ctx) + std::string("\004") + std::string(msgid)).c_str(); std::string msg_ctxt_id = (std::string(ctx) + std::string("\004") + std::string(msgid));
const char *translation = gettext(msg_ctxt_id); const char *translation = gettext(msg_ctxt_id.c_str());
if (translation == msg_ctxt_id) { if (std::string(translation) == msg_ctxt_id) {
return msgid; return msgid;
} else { } else {
return translation; return translation;
@ -20,4 +20,7 @@ inline static const char *gettext_ctx(const char *ctx, const char *msgid) {
#define _TRI(icon, str) _TRIS(icon, str).c_str() #define _TRI(icon, str) _TRIS(icon, str).c_str()
#define _TRIS_CTX(icon, ctx, str) (std::string(icon) + _TRS_CTX(ctx, str)) #define _TRIS_CTX(icon, ctx, str) (std::string(icon) + _TRS_CTX(ctx, str))
#define _TRI_CTX(icon, ctx, str) _TRIS_CTX(icon, ctx, str).c_str() #define _TRI_CTX(icon, ctx, str) _TRIS_CTX(icon, ctx, str).c_str()
#define CURRENT_LANGUAGE textdomain(NULL) #define CURRENT_LANGUAGE setlocale(LC_MESSAGES, NULL)
// The value required to set the operating system's default language.
#define DEFAULT_LANG ""
#define SET_LANG(lang) setlocale(LC_MESSAGES, lang)