2024-03-23 18:41:26 -07:00
# include "main.h"
# include <data.h>
# include <util.hpp>
# include <assets/assets.h>
# include <filesystem>
# include <options.hpp>
2024-10-19 10:19:08 -07:00
# include "imgui/imgui.h"
2024-03-23 18:41:26 -07:00
# include "ui_backend.hpp"
# include "thirdparty/CLI11.hpp"
2024-12-11 12:56:17 -08:00
# include "imgui/misc/cpp/imgui_stdlib.h"
2024-05-02 14:52:11 -07:00
# include <web_functions.hpp>
2024-04-28 12:31:40 -07:00
using namespace Looper : : Options ;
2024-03-23 18:41:26 -07:00
void MainLoop : : Init ( ) {
# ifdef PORTALS
g_set_application_name ( " Looper " ) ;
# endif
// Our state
show_demo_window = false ;
FileBrowser fileDialog ( false , ImGuiFileBrowserFlags_NoTitleBar | ImGuiFileBrowserFlags_NoMove | ImGuiFileBrowserFlags_NoResize ) ;
2024-04-24 09:59:51 -07:00
# ifndef __EMSCRIPTEN__
2024-03-23 18:41:26 -07:00
fileDialog . SetPwd ( path ( userdir ) / path ( " Music " ) ) ;
2024-04-24 09:59:51 -07:00
# endif
2024-03-23 18:41:26 -07:00
fileDialog . SetWindowSize ( window_width , window_height ) ;
//fileDialog.SetWindowPos(0, 0);
position = 0.0 ;
prefs_window = false ;
theme_editor = false ;
stopped = true ;
about_window = false ;
string lang ;
{
Json : : Value config ;
std : : ifstream stream ;
2024-04-28 12:31:40 -07:00
path jsonConfigPath = path ( prefPath ) / " config.json " ;
stream . open ( jsonConfigPath ) ;
2024-03-23 18:41:26 -07:00
if ( stream . is_open ( ) ) {
stream > > config ;
if ( config . isMember ( " theme_name " ) ) {
2024-04-28 12:31:40 -07:00
init_option < std : : string > ( " ui.imgui.theme " , config [ " theme_name " ] . asString ( ) ) ;
2024-03-23 18:41:26 -07:00
}
if ( config . isMember ( " accent_color " ) ) {
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 ( ) ) ;
}
2024-04-28 12:31:40 -07:00
toml : : table accent_color_table ;
accent_color_table . insert ( " h " , accent_color . x ) ;
accent_color_table . insert ( " s " , accent_color . y ) ;
accent_color_table . insert ( " v " , accent_color . z ) ;
accent_color_table . insert ( " a " , accent_color . w ) ;
init_option < toml : : table > ( " ui.imgui.accent_color " , accent_color_table ) ;
2024-03-23 18:41:26 -07:00
}
if ( config . isMember ( " demo_window " ) ) {
2024-04-28 12:31:40 -07:00
init_option < bool > ( " ui.imgui.demo_window " , config [ " demo_window " ] . asBool ( ) ) ;
2024-03-23 18:41:26 -07:00
}
if ( config . isMember ( " vsync " ) ) {
2024-04-28 12:31:40 -07:00
init_option < bool > ( " ui.imgui.vsync " , config [ " vsync " ] . asBool ( ) ) ;
2024-03-23 18:41:26 -07:00
}
if ( config . isMember ( " framerate " ) ) {
2024-04-28 12:31:40 -07:00
init_option < int64_t > ( " ui.imgui.framerate " , ( int64_t ) config [ " framerate " ] . asUInt ( ) ) ;
2024-03-23 18:41:26 -07:00
}
if ( config . isMember ( " lang " ) ) {
Json : : Value langValue ;
2024-04-28 12:31:40 -07:00
if ( ! langValue . isNull ( ) ) {
init_option < std : : string > ( " ui.imgui.lang " , config [ " lang " ] . asString ( ) ) ;
2024-03-23 18:41:26 -07:00
}
}
stream . close ( ) ;
2024-04-28 12:31:40 -07:00
std : : remove ( jsonConfigPath . c_str ( ) ) ;
}
{
std : : string themeName = get_option < std : : string > ( " ui.imgui.theme " , " light " ) ;
path themePath = Theme : : themeDir / path ( themeName + " .toml " ) ;
2024-05-01 09:07:08 -07:00
# ifdef __EMSCRIPTEN__
try {
auto newTheme = new Theme ( themePath ) ;
delete theme ;
theme = newTheme ;
} catch ( std : : exception _ ) {
2024-10-18 10:45:22 -07:00
2024-05-01 09:07:08 -07:00
}
# else
2024-04-28 12:31:40 -07:00
if ( exists ( themePath ) ) {
delete theme ;
theme = new Theme ( themePath ) ;
}
2024-05-01 09:07:08 -07:00
# endif
2024-04-28 12:31:40 -07:00
if ( option_set ( " ui.imgui.lang " ) ) {
lang = get_option < std : : string > ( " ui.imgui.lang " ) ;
} else {
lang = DEFAULT_LANG ;
}
SET_LANG ( lang . c_str ( ) ) ;
show_demo_window = get_option < bool > ( " ui.imgui.demo_window " , false ) ;
vsync = get_option < bool > ( " ui.imgui.vsync " , true ) ;
framerate = ( unsigned ) get_option < int64_t > ( " ui.imgui.framerate " , 60 ) ;
accent_color . x = ( float ) get_option < double > ( " ui.imgui.accent_color.h " , accent_color . x ) ;
accent_color . y = ( float ) get_option < double > ( " ui.imgui.accent_color.s " , accent_color . y ) ;
accent_color . z = ( float ) get_option < double > ( " ui.imgui.accent_color.v " , accent_color . z ) ;
accent_color . w = ( float ) get_option < double > ( " ui.imgui.accent_color.a " , accent_color . w ) ;
2024-10-14 21:27:16 -07:00
debug_mode = get_option < bool > ( " ui.imgui.debug_mode " , false ) ;
2024-04-28 12:31:40 -07:00
}
2024-05-01 09:07:08 -07:00
Theme : : updateAvailableThemes ( ) ;
if ( Theme : : availableThemes . empty ( ) ) {
2024-03-23 18:41:26 -07:00
path lightPath = Theme : : themeDir / " light.toml " ;
path darkPath = Theme : : themeDir / " dark.toml " ;
string builtinDescription = _TRS_CTX ( " Built-in themes | Theme default strings | name " , " (built-in) " ) ;
if ( ! exists ( lightPath ) ) {
Theme light ( false ) ;
ThemeStrings & strings = light . strings [ " fallback " ] ;
strings . name = _TRS_CTX ( " Built-in light theme | Theme default strings | name " , " Default light " ) ;
strings . description = builtinDescription ;
light . strings [ CURRENT_LANGUAGE ] = strings ;
light . Save ( lightPath ) ;
}
if ( ! exists ( darkPath ) ) {
Theme dark ( true ) ;
ThemeStrings & strings = dark . strings [ " fallback " ] ;
strings . name = _TRS_CTX ( " Built-in dark theme | Theme default strings | name " , " Default dark " ) ;
strings . description = builtinDescription ;
dark . strings [ CURRENT_LANGUAGE ] = strings ;
dark . Save ( darkPath ) ;
}
delete theme ;
theme = new Theme ( darkPath ) ;
}
}
2024-05-01 09:07:08 -07:00
theme - > Apply ( accent_color , ( float ) scale ) ;
2024-04-24 09:59:51 -07:00
FileLoaded ( ) ;
2024-03-23 18:41:26 -07:00
}
void MainLoop : : Drop ( std : : string file ) {
LoadFile ( file ) ;
}
2024-04-24 09:59:51 -07:00
void MainLoop : : FileLoaded ( ) {
auto file_maybe = playback - > get_current_title ( ) ;
if ( file_maybe . has_value ( ) ) {
auto name = file_maybe . value ( ) ;
SetWindowTitle ( ( name + std : : string ( " - Looper " ) ) . c_str ( ) ) ;
} else {
SetWindowTitle ( " Looper " ) ;
}
streams = playback - > get_streams ( ) ;
2024-10-19 10:19:08 -07:00
properties = playback - > get_property_list ( ) ;
boolean_properties . clear ( ) ;
double_properties . clear ( ) ;
2024-04-24 09:59:51 -07:00
}
2024-03-23 18:41:26 -07:00
void MainLoop : : GuiFunction ( ) {
2024-04-28 12:31:40 -07:00
# if defined(__EMSCRIPTEN__)||defined(__ANDROID__)
2024-04-24 09:59:51 -07:00
playback - > LoopHook ( ) ;
2024-04-28 12:31:40 -07:00
# endif
2024-03-23 18:41:26 -07:00
position = playback - > GetPosition ( ) ;
length = playback - > GetLength ( ) ;
// Set the window title if the file changed, or playback stopped.
2024-04-24 09:59:51 -07:00
if ( playback - > handle_signals ( PlaybackSignalFileChanged | PlaybackSignalStarted | PlaybackSignalStopped ) ) {
FileLoaded ( ) ;
2024-03-23 18:41:26 -07:00
}
bool lengthKnown = length > 0.0 ;
2024-09-28 10:31:06 -07:00
auto dockid = ImGui : : DockSpaceOverViewport ( 0 , nullptr , ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_AutoHideTabBar ) ;
2024-03-23 18:41:26 -07:00
// 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 ( ) ;
}
2024-05-02 14:52:11 -07:00
# ifdef __EMSCRIPTEN__
if ( serviceworker_registered ( ) ) {
if ( ImGui : : MenuItem ( _TRI_CTX ( ICON_FK_DOWNLOAD , " Main menu | File " , " Update " ) ) ) {
update ( ) ;
}
}
if ( is_puter_enabled ( ) ) {
# endif
2024-03-23 18:41:26 -07:00
if ( ImGui : : MenuItem ( _TRI_CTX ( ICON_FK_WINDOW_CLOSE , " Main menu | File " , " Quit " ) ) ) {
done = true ;
}
2024-05-02 14:52:11 -07:00
# ifdef __EMSCRIPTEN__
}
# endif
2024-03-23 18:41:26 -07:00
ImGui : : EndMenu ( ) ;
}
if ( ImGui : : BeginMenu ( _TRI_CTX ( ICON_FK_SCISSORS , " Main menu " , " Edit " ) ) ) {
if ( ImGui : : MenuItem ( _TRI_CTX ( ICON_FK_COG , " Main menu | Edit " , " Preferences... " ) ) ) {
prefs_window = true ;
}
ImGui : : EndMenu ( ) ;
}
2024-10-14 21:27:16 -07:00
# ifndef DEBUG_MODE
if ( debug_mode )
# endif
2024-03-23 18:41:26 -07:00
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 ;
2024-05-01 09:07:08 -07:00
set_option < double > ( " ui.imgui.demo_window " , show_demo_window ) ;
2024-03-23 18:41:26 -07:00
}
2024-10-18 10:45:22 -07:00
if ( ImGui : : MenuItem ( _TR_CTX ( " Main menu | Debug " , " Edit properties " ) , nullptr , property_editor ) ) {
property_editor = ! property_editor ;
}
2024-10-19 10:19:08 -07:00
ImGui : : EndMenu ( ) ;
2024-03-23 18:41:26 -07:00
}
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 ) ;
{
2024-04-13 12:56:39 -07:00
float centerSpace = ImGui : : GetWindowHeight ( ) - ImGui : : GetFrameHeightWithSpacing ( ) - ImGui : : GetFrameHeight ( ) - ImGui : : GetStyle ( ) . WindowPadding . y ;
if ( streams . size ( ) > 0 ) {
static string filter = " " ;
ImGui : : TextUnformatted ( _TR_CTX ( " Main Window | Stream selector | Filter label " , " Filter: " ) ) ; ImGui : : SameLine ( ) ;
ImGui : : SetNextItemWidth ( ImGui : : GetWindowWidth ( ) - ImGui : : GetCursorPosX ( ) - ImGui : : GetStyle ( ) . WindowPadding . x ) ;
ImGui : : InputText ( " ##FilterInput " , & filter ) ;
ImGui : : TextUnformatted ( _TR_CTX ( " Main Window | Stream selector | Selector label " , " Select a stream to switch to... " ) ) ;
ImVec2 ChildSize = ImVec2 ( ImGui : : GetWindowWidth ( ) - ( ImGui : : GetStyle ( ) . WindowPadding . x * 2.0f ) , centerSpace - ( ImGui : : GetFrameHeightWithSpacing ( ) * 2.0f ) - ( ImGui : : GetStyle ( ) . ItemSpacing . y * 2.0f ) ) ;
if ( ImGui : : BeginChildFrame ( ImGui : : GetID ( " ##StreamsContainer " ) , ChildSize ) ) {
ImVec2 TableSize = ImVec2 ( 0 , 0 ) ;
if ( ImGui : : BeginTable ( " ##Streams " , 2 , ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_ScrollY , TableSize ) ) {
// Text in TableSetupColumn calls not translated because they're not visible to the user.
ImGui : : TableSetupColumn ( " Index " , 0 ) ;
ImGui : : TableSetupColumn ( " Name " , ImGuiTableColumnFlags_WidthStretch ) ;
for ( int i = 0 ; i < streams . size ( ) ; i + + ) {
auto & stream = streams [ i ] ;
2024-04-14 13:25:49 -07:00
if ( stream . name = = " " & & stream . length = = 0 ) {
2024-04-13 12:56:39 -07:00
continue ;
}
2024-04-14 13:25:49 -07:00
if ( stream . name . starts_with ( filter ) ) {
2024-04-13 12:56:39 -07:00
ImGui : : TableNextRow ( ) ;
ImGui : : TableSetColumnIndex ( 0 ) ;
2024-04-14 13:25:49 -07:00
ImGui : : Text ( " %i " , stream . id ) ;
2024-04-13 12:56:39 -07:00
ImGui : : TableSetColumnIndex ( 1 ) ;
const bool is_selected = playback - > get_current_stream ( ) = = i ;
2024-04-14 13:25:49 -07:00
if ( ImGui : : Selectable ( stream . name . c_str ( ) , is_selected , 0 ) ) {
length = stream . length ;
2024-04-13 12:56:39 -07:00
playback - > play_stream ( i ) ;
}
if ( is_selected ) {
ImGui : : SetItemDefaultFocus ( ) ;
}
}
}
ImGui : : EndTable ( ) ;
}
}
ImGui : : EndChildFrame ( ) ;
}
2024-03-23 18:41:26 -07:00
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 ;
2024-10-18 10:45:22 -07:00
2024-03-26 18:39:02 -07:00
double seek_bar_width = - ( ImGui : : GetFontSize ( ) * ( 1 + ( 8 * NEXT_SLIDER_COUNT ) ) ) - ( ( ImGui : : GetStyle ( ) . ItemSpacing . x + ImGui : : GetStyle ( ) . FramePadding . x ) * ( NEXT_SLIDER_COUNT + 1 ) ) ;
2024-10-18 10:45:22 -07:00
2024-03-23 18:41:26 -07:00
if ( lengthKnown ) {
2024-03-26 18:39:02 -07:00
ImGui : : SetNextItemWidth ( seek_bar_width ) ;
2024-03-23 18:41:26 -07:00
uint8_t components = TimeToComponentCount ( playback - > GetLength ( ) ) ;
2024-03-26 18:39:02 -07:00
string time_str = TimeToString ( position , components ) + " / " + TimeToString ( length , components ) ;
2024-03-23 18:41:26 -07:00
if ( ImGui : : SliderFloat ( " ##Seek " , & position , 0.0f , playback - > GetLength ( ) , time_str . c_str ( ) , ImGuiSliderFlags_NoRoundToFormat ) )
playback - > Seek ( position ) ;
} else {
2024-03-26 18:39:02 -07:00
ImVec2 size ( seek_bar_width , ImGui : : GetFrameHeight ( ) ) ;
ImGui : : InvisibleButton ( " ##SeekIndeterminate " , size ) ;
2024-03-23 18:41:26 -07:00
}
ImGui : : SameLine ( ) ;
if ( ImGui : : Button ( ICON_FK_STOP " ##Stop " ) ) {
playback - > Stop ( ) ;
}
2024-03-26 18:39:02 -07:00
float pitch = playback - > GetPitch ( ) , tempo = playback - > GetTempo ( ) , speed = playback - > GetSpeed ( ) , volume = playback - > GetVolume ( ) ;
2024-03-23 18:41:26 -07:00
ImGui : : SameLine ( ) ;
ImGui : : SetNextItemWidth ( ImGui : : GetFontSize ( ) * 8 ) ;
2024-03-26 18:39:02 -07:00
if ( ImGui : : SliderFloat ( " ##Volume " , & volume , 0.0 , 100.0 , ICON_FK_VOLUME_UP " : %.0f%% " ) ) {
playback - > SetVolume ( volume ) ;
2024-03-23 18:41:26 -07:00
}
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 ) ) ;
2024-03-26 18:39:02 -07:00
if ( ImGui : : SliderFloat ( " ##Speed " , & speed , 0.25 , 4.0 , _TR_CTX ( " Playback controls | slider " , " Speed: %.2fx " ) , ImGuiSliderFlags_Logarithmic ) ) {
playback - > SetSpeed ( speed ) ;
2024-03-23 18:41:26 -07:00
}
ImGui : : SameLine ( ) ;
2024-03-26 18:39:02 -07:00
if ( ImGui : : SliderFloat ( " ##Tempo " , & tempo , 0.25 , 4.0 , _TR_CTX ( " Playback controls | slider " , " Tempo: %.2fx " ) , ImGuiSliderFlags_Logarithmic ) ) {
playback - > SetTempo ( tempo ) ;
2024-03-23 18:41:26 -07:00
}
ImGui : : SameLine ( ) ;
2024-03-26 18:39:02 -07:00
if ( ImGui : : SliderFloat ( " ##Pitch " , & pitch , 0.25 , 4.0 , _TR_CTX ( " Playback controls | slider " , " Pitch: %.2fx " ) , ImGuiSliderFlags_Logarithmic ) ) {
playback - > SetPitch ( pitch ) ;
2024-03-23 18:41:26 -07:00
}
ImGui : : PopItemWidth ( ) ;
}
ImGui : : End ( ) ;
2024-10-18 10:45:22 -07:00
if ( property_editor ) {
ImGui : : SetNextWindowDockID ( dockid ) ;
ImGui : : Begin ( " Property Editor " , & property_editor ) ;
{
2024-10-19 10:19:08 -07:00
for ( auto property : properties ) {
ImGui : : PushID ( property . path ( ) . c_str ( ) ) ;
bool valid = false ;
switch ( property . type ( ) ) {
2024-12-11 12:56:17 -08:00
case PropertyType : : String : {
std : : string value = " " ;
if ( string_properties . contains ( property . path ( ) ) ) {
value = string_properties [ property . path ( ) ] ;
} else {
auto value_to_resolve = playback - > get_property ( property . path ( ) ) ;
if ( value_to_resolve . has_value ( ) ) {
value = resolve_value < std : : string > ( value_to_resolve . value ( ) ) ;
}
string_properties [ property . path ( ) ] = value ;
}
ImGui : : InputText ( property . path ( ) . c_str ( ) , & value ) ;
string_properties [ property . path ( ) ] = value ;
valid = true ;
} break ;
case PropertyType : : Int : {
std : : optional < double > min ;
std : : optional < double > max ;
int value = 0 ;
if ( int_properties . contains ( property . path ( ) ) ) {
value = int_properties [ property . path ( ) ] ;
} else {
auto value_to_resolve = playback - > get_property ( property . path ( ) ) ;
if ( value_to_resolve . has_value ( ) ) {
value = resolve_value < int > ( value_to_resolve . value ( ) ) ;
}
int_properties [ property . path ( ) ] = value ;
}
if ( property . has_hint ( ) & & property . hint ( ) . has_range ( ) ) {
auto range = property . hint ( ) . range ( ) ;
if ( range . has_min ( ) & & range . has_max ( ) ) {
if ( ImGui : : SliderInt ( property . path ( ) . c_str ( ) , & value , ( int ) range . min ( ) , ( int ) range . max ( ) ) ) {
int_properties [ property . path ( ) ] = value ;
}
valid = true ;
} else {
if ( range . has_min ( ) ) min = range . min ( ) ;
if ( range . has_max ( ) ) max = range . max ( ) ;
}
}
if ( ! valid ) {
ImGui : : InputInt ( property . path ( ) . c_str ( ) , & value ) ;
if ( min . has_value ( ) & & value < min ) {
value = min . value ( ) ;
}
if ( max . has_value ( ) & & value > max ) {
value = max . value ( ) ;
}
int_properties [ property . path ( ) ] = value ;
valid = true ;
}
} break ;
2024-10-19 10:19:08 -07:00
case PropertyType : : Double : {
std : : optional < double > min ;
std : : optional < double > max ;
double value = 0.0 ;
if ( double_properties . contains ( property . path ( ) ) ) {
value = double_properties [ property . path ( ) ] ;
} else {
auto value_to_resolve = playback - > get_property ( property . path ( ) ) ;
if ( value_to_resolve . has_value ( ) ) {
value = resolve_value < double > ( value_to_resolve . value ( ) ) ;
}
double_properties [ property . path ( ) ] = value ;
}
if ( property . has_hint ( ) & & property . hint ( ) . has_range ( ) ) {
auto range = property . hint ( ) . range ( ) ;
if ( range . has_min ( ) & & range . has_max ( ) ) {
float flt = ( float ) value ;
if ( ImGui : : SliderFloat ( property . path ( ) . c_str ( ) , & flt , ( float ) range . min ( ) , ( float ) range . max ( ) ) ) {
double_properties [ property . path ( ) ] = flt ;
}
valid = true ;
} else {
if ( range . has_min ( ) ) min = range . min ( ) ;
if ( range . has_max ( ) ) max = range . max ( ) ;
}
}
if ( ! valid ) {
ImGui : : InputDouble ( property . path ( ) . c_str ( ) , & value ) ;
if ( min . has_value ( ) & & value < min ) {
value = min . value ( ) ;
}
if ( max . has_value ( ) & & value > max ) {
value = max . value ( ) ;
}
double_properties [ property . path ( ) ] = value ;
valid = true ;
}
} break ;
case PropertyType : : Boolean : {
bool value = false ;
if ( boolean_properties . contains ( property . path ( ) ) ) {
value = boolean_properties [ property . path ( ) ] ;
} else {
auto value_to_resolve = playback - > get_property ( property . path ( ) ) ;
if ( value_to_resolve . has_value ( ) ) {
value = resolve_value < bool > ( value_to_resolve . value ( ) ) ;
}
boolean_properties [ property . path ( ) ] = value ;
}
if ( ImGui : : Checkbox ( property . path ( ) . c_str ( ) , & value ) ) {
boolean_properties [ property . path ( ) ] = value ;
}
valid = true ;
} break ;
}
if ( valid ) {
ImGui : : SameLine ( ) ;
if ( ImGui : : Button ( " Set " ) ) {
switch ( property . type ( ) ) {
2024-12-11 12:56:17 -08:00
case PropertyType : : String : {
StringProperty property_s ;
property_s . set_value ( string_properties [ property . path ( ) ] ) ;
google : : protobuf : : Any value ;
value . PackFrom ( property_s ) ;
playback - > set_property ( property . path ( ) , value ) ;
} break ;
case PropertyType : : Int : {
IntProperty property_i ;
property_i . set_value ( int_properties [ property . path ( ) ] ) ;
google : : protobuf : : Any value ;
value . PackFrom ( property_i ) ;
playback - > set_property ( property . path ( ) , value ) ;
} break ;
2024-10-19 10:19:08 -07:00
case PropertyType : : Double : {
DoubleProperty property_d ;
property_d . set_value ( double_properties [ property . path ( ) ] ) ;
google : : protobuf : : Any value ;
value . PackFrom ( property_d ) ;
playback - > set_property ( property . path ( ) , value ) ;
} break ;
case PropertyType : : Boolean : {
BooleanProperty property_b ;
property_b . set_value ( boolean_properties [ property . path ( ) ] ) ;
google : : protobuf : : Any value ;
value . PackFrom ( property_b ) ;
playback - > set_property ( property . path ( ) , value ) ;
} break ;
}
}
}
ImGui : : PopID ( ) ;
}
2024-10-18 10:45:22 -07:00
}
ImGui : : End ( ) ;
}
2024-03-23 18:41:26 -07:00
if ( prefs_window ) {
ImGui : : SetNextWindowDockID ( dockid ) ;
ImGui : : Begin ( _TRI_CTX ( ICON_FK_COG , " Window title, window opened by menu item " , " Preferences... " ) , & prefs_window ) ;
{
2024-10-18 10:45:22 -07:00
2024-03-23 18:41:26 -07:00
static std : : string set_backend_name = cur_backend - > get_name ( ) ;
static std : : string set_backend_id = cur_backend - > get_id ( ) ;
2024-03-26 18:39:02 -07:00
if ( restart_needed ) {
ImGui : : TextUnformatted ( _TR ( " A restart is needed to apply some changes. " ) ) ;
}
if ( ImGui : : BeginCombo ( _TR ( " UI frontend " ) , set_backend_name . c_str ( ) ) ) {
2024-03-23 18:41:26 -07:00
for ( auto & backend : backends ) {
bool is_current = set_backend_id = = backend - > get_id ( ) ;
if ( ImGui : : Selectable ( backend - > get_name ( ) . c_str ( ) , & is_current ) ) {
set_backend_id = backend - > get_id ( ) ;
set_backend_name = backend - > get_name ( ) ;
2024-03-26 18:39:02 -07:00
Looper : : Options : : set_option ( " ui.frontend " , set_backend_id ) ;
restart_needed = true ;
2024-03-23 18:41:26 -07:00
}
if ( is_current ) {
ImGui : : SetItemDefaultFocus ( ) ;
}
}
ImGui : : EndCombo ( ) ;
}
2024-05-01 09:07:08 -07:00
if ( ImGui : : Checkbox ( _TR_CTX ( " Preference | VSync checkbox " , " Enable VSync " ) , & vsync ) ) {
set_option < bool > ( " ui.imgui.vsync " , vsync ) ;
}
2024-03-23 18:41:26 -07:00
ImGui : : SameLine ( ) ;
ImGui : : SetNextItemWidth ( ImGui : : GetWindowWidth ( ) - ImGui : : GetCursorPosX ( ) - ImGui : : GetStyle ( ) . WindowPadding . x ) ;
2024-05-01 09:07:08 -07:00
if ( ImGui : : SliderInt ( " ##Framerate " , & framerate , 10 , 480 , _TR_CTX ( " Preferences | Framerate slider " , " Max framerate without VSync: %d " ) ) ) {
set_option < int64_t > ( " ui.imgui.framerate " , framerate ) ;
}
2024-10-14 21:27:16 -07:00
if ( ImGui : : Checkbox ( _TR_CTX ( " Preference | Debug menu enable " , " Enable debug menu in release builds " ) , & debug_mode ) ) {
set_option < bool > ( " ui.imgui.debug_mode " , debug_mode ) ;
}
2024-03-23 18:41:26 -07:00
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 ;
}
2024-04-24 09:59:51 -07:00
# ifdef __EMSCRIPTEN__
bool puterEnabled = is_puter_enabled ( ) ;
if ( ImGui : : Checkbox ( " Enable Puter API " , & puterEnabled ) ) {
enable_puter ( puterEnabled ) ;
}
# endif
2024-03-23 18:41:26 -07:00
static bool override_lang = lang ! = DEFAULT_LANG ;
if ( ImGui : : Checkbox ( _TR_CTX ( " Preference | override enable checkbox " , " Override language " ) , & override_lang ) ) {
if ( ! override_lang ) {
lang = DEFAULT_LANG ;
SET_LANG ( lang . c_str ( ) ) ;
}
2024-05-01 09:07:08 -07:00
if ( lang = = DEFAULT_LANG ) {
delete_option ( " ui.imgui.lang " ) ;
} else {
set_option < std : : string > ( " ui.imgui.lang " , lang ) ;
}
2024-03-23 18:41:26 -07:00
}
if ( override_lang ) {
ImGui : : SameLine ( ) ;
ImGui : : SetNextItemWidth ( ImGui : : GetWindowWidth ( ) - ImGui : : GetCursorPosX ( ) - ( ImGui : : GetFontSize ( ) ) - ( ( ImGui : : GetStyle ( ) . ItemSpacing . x + ( ImGui : : GetStyle ( ) . FramePadding . x * 2.0f ) ) ) - ( ImGui : : GetStyle ( ) . WindowPadding . x ) ) ;
ImGui : : InputText ( " ##LanguageOverrideTextBox " , & lang ) ;
ImGui : : SameLine ( ) ;
if ( ImGui : : Button ( ICON_FK_CHECK ) ) {
SET_LANG ( lang . c_str ( ) ) ;
2024-05-01 09:07:08 -07:00
if ( lang = = DEFAULT_LANG ) {
delete_option ( " ui.imgui.lang " ) ;
} else {
set_option < std : : string > ( " ui.imgui.lang " , lang ) ;
}
2024-03-23 18:41:26 -07:00
}
}
2024-04-28 12:31:40 -07:00
bool overrideTouchscreenMode = touchScreenModeOverride . has_value ( ) ;
if ( ImGui : : Checkbox ( _TR_CTX ( " Preference | override enable checkbox " , " Override touchscreen mode " ) , & overrideTouchscreenMode ) ) {
if ( overrideTouchscreenMode ) {
touchScreenModeOverride = isTouchScreenMode ( ) ;
} else {
touchScreenModeOverride = { } ;
}
}
if ( overrideTouchscreenMode ) {
bool touchScreenMode = isTouchScreenMode ( ) ;
2024-05-01 09:07:08 -07:00
if ( ImGui : : Checkbox ( _TRI_CTX ( ICON_FK_HAND_O_UP , " Preference | Checkbox (Only shown when override enabled) " , " Enable touch screen mode " ) , & touchScreenMode ) ) {
2024-04-28 12:31:40 -07:00
touchScreenModeOverride = touchScreenMode ;
}
}
2024-05-01 09:07:08 -07:00
bool overrideScale = scaleOverride . has_value ( ) ;
if ( ImGui : : Checkbox ( _TR_CTX ( " Preference | override enable checkbox " , " Override DPI scaling " ) , & overrideScale ) ) {
if ( overrideScale ) {
scaleOverride = scale ;
} else {
scaleOverride = { } ;
}
QueueUpdateScale ( ) ;
}
if ( overrideScale ) {
float newScale = scale ;
if ( ImGui : : SliderFloat ( _TRI_CTX ( ICON_FK_HAND_O_UP , " Preference | Slider (Only shown when override enabled) " , " DPI Scaling amount " ) , & newScale , 0.25 , 4 , " %.2f " , ImGuiSliderFlags_Logarithmic ) ) {
scaleOverride = newScale ;
QueueUpdateScale ( ) ;
}
}
2024-04-28 12:31:40 -07:00
2024-03-23 18:41:26 -07:00
static string filter = " " ;
2024-04-10 13:25:18 -07:00
ImGui : : TextUnformatted ( _TR_CTX ( " Preference | Theme selector | Filter label " , " Filter: " ) ) ; ImGui : : SameLine ( ) ;
2024-03-23 18:41:26 -07:00
ImGui : : SetNextItemWidth ( ImGui : : GetWindowWidth ( ) - ImGui : : GetCursorPosX ( ) - ImGui : : GetStyle ( ) . WindowPadding . x ) ;
ImGui : : InputText ( " ##FilterInput " , & filter ) ;
2024-04-10 13:25:18 -07:00
ImGui : : TextUnformatted ( _TR_CTX ( " Preferences | Theme selector | Selector label " , " Select a theme... " ) ) ;
2024-03-23 18:41:26 -07:00
ImVec2 ChildSize = ImVec2 ( ImGui : : GetWindowWidth ( ) - ( ImGui : : GetStyle ( ) . WindowPadding . x * 2.0f ) , - ImGui : : GetFrameHeightWithSpacing ( ) ) ;
if ( ImGui : : BeginChildFrame ( ImGui : : GetID ( " ##ThemesContainer " ) , ChildSize ) ) {
ImVec2 TableSize = ImVec2 ( 0 , 0 ) ;
if ( ImGui : : BeginTable ( " ##Themes " , 2 , ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_ScrollY , TableSize ) ) {
// Text in TableSetupColumn calls not translated because they're not visible to the user.
ImGui : : TableSetupColumn ( " Name " , ImGuiTableColumnFlags_WidthStretch ) ;
ImGui : : TableSetupColumn ( " Remove " , 0 ) ;
for ( auto themePath : Theme : : availableThemes ) {
string themeStem = themePath . stem ( ) . string ( ) ;
if ( themeStem . starts_with ( filter ) ) {
ImGui : : TableNextRow ( ) ;
ImGui : : TableSetColumnIndex ( 0 ) ;
const bool is_selected = themePath = = theme - > file_path ;
if ( ImGui : : Selectable ( ( theme - > themeStrings [ themePath ] . name + string ( " ( " ) + string ( themeStem ) + string ( " ) " ) ) . c_str ( ) , is_selected , 0 ) ) {
delete theme ;
theme = new Theme ( themePath ) ;
2024-05-01 09:07:08 -07:00
theme - > Apply ( accent_color , ( float ) scale ) ;
path themeFName = themePath . stem ( ) ;
if ( ! themeFName . empty ( ) ) {
set_option < std : : string > ( " ui.imgui.theme " , themeFName . string ( ) ) ;
}
2024-03-23 18:41:26 -07:00
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 ( ) ;
}
}
2024-10-18 10:45:22 -07:00
ImGui : : EndChildFrame ( ) ;
2024-03-23 18:41:26 -07:00
ImGui : : SetNextItemWidth ( ImGui : : GetWindowWidth ( ) - ( ImGui : : GetStyle ( ) . WindowPadding . x * 2 ) ) ;
2024-05-01 09:07:08 -07:00
if ( ImGui : : ColorEdit4 ( " ##AccentColor " , & accent_color . x , ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_Float ) ) {
set_option < double > ( " ui.imgui.accent_color.h " , accent_color . x ) ;
set_option < double > ( " ui.imgui.accent_color.s " , accent_color . y ) ;
set_option < double > ( " ui.imgui.accent_color.v " , accent_color . z ) ;
set_option < double > ( " ui.imgui.accent_color.a " , accent_color . w ) ;
}
theme - > Apply ( accent_color , ( float ) scale ) ;
2024-03-23 18:41:26 -07:00
}
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. " , " Looper " ) ;
ImGui : : SetCursorPosX ( ( ImGui : : GetWindowWidth ( ) - ImGui : : GetFont ( ) - > CalcTextSizeA ( ImGui : : GetFontSize ( ) , FLT_MAX , 0.0f , APP_NAME_STR . c_str ( ) ) . x ) / 2.0f ) ;
2024-04-10 13:25:18 -07:00
ImGui : : TextUnformatted ( APP_NAME_STR . c_str ( ) ) ;
2024-03-23 18:41:26 -07:00
ImGui : : PopFont ( ) ;
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 ) ;
2024-04-10 13:25:18 -07:00
ImGui : : TextUnformatted ( VER_STRING . c_str ( ) ) ;
2024-03-23 18:41:26 -07:00
ImGui : : NewLine ( ) ;
2024-03-26 18:39:02 -07:00
auto & license_data = get_license_data ( ) ;
2024-03-23 18:41:26 -07:00
// Left
2024-03-26 18:39:02 -07:00
static LicenseData selected = * license_data . begin ( ) ;
2024-03-23 18:41:26 -07:00
{
ImGui : : BeginGroup ( ) ;
ImGui : : TextUnformatted ( _TR_CTX ( " Project selector label. " , " Project " ) ) ;
// Next string is internal.
ImGui : : BeginChild ( " project selector " , ImVec2 ( 150 , 0 ) , true ) ;
2024-03-26 18:39:02 -07:00
for ( auto project : license_data )
2024-03-23 18:41:26 -07:00
{
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.
2024-05-01 09:07:08 -07:00
theme - > Apply ( accent_color , ( float ) scale ) ;
2024-03-23 18:41:26 -07:00
}
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 ( ) ) {
playback - > Start ( fileDialog . GetSelected ( ) . string ( ) ) ;
// Make sure to not load the file unnecessarily.
fileDialog . ClearSelected ( ) ;
}
2024-04-09 10:15:05 -07:00
if ( exit_flag . load ( ) ) {
done = true ;
}
2024-03-23 18:41:26 -07:00
}
void MainLoop : : LoadFile ( std : : string file ) {
playback - > Start ( file ) ;
}
void MainLoop : : Deinit ( ) {
{
path themePath ( theme - > file_path ) ;
2024-05-01 09:07:08 -07:00
themePath = themePath . stem ( ) ;
2024-03-23 18:41:26 -07:00
if ( ! themePath . empty ( ) ) {
2024-05-01 09:07:08 -07:00
set_option < std : : string > ( " ui.imgui.theme " , themePath . string ( ) ) ;
2024-03-23 18:41:26 -07:00
}
2024-04-28 12:31:40 -07:00
set_option < double > ( " ui.imgui.accent_color.h " , accent_color . x ) ;
set_option < double > ( " ui.imgui.accent_color.s " , accent_color . y ) ;
set_option < double > ( " ui.imgui.accent_color.v " , accent_color . z ) ;
set_option < double > ( " ui.imgui.accent_color.a " , accent_color . w ) ;
set_option < double > ( " ui.imgui.demo_window " , show_demo_window ) ;
set_option < bool > ( " ui.imgui.vsync " , vsync ) ;
set_option < int64_t > ( " ui.imgui.framerate " , framerate ) ;
2024-03-23 18:41:26 -07:00
if ( lang = = DEFAULT_LANG ) {
2024-04-28 12:31:40 -07:00
delete_option ( " ui.imgui.lang " ) ;
2024-03-23 18:41:26 -07:00
} else {
2024-04-28 12:31:40 -07:00
set_option < std : : string > ( " ui.imgui.lang " , lang ) ;
2024-03-23 18:41:26 -07:00
}
}
}
MainLoop : : MainLoop ( ) : RendererBackend ( ) {
for ( auto & kv : UIBackend : : backends ) {
backends . push_back ( kv . second ) ;
}
cur_backend = UIBackend : : get_backend ( UI_BACKEND ( ) ) . value_or ( UIBackend : : get_first_backend ( ) ) ;
}
std : : string ImGuiUIBackend : : get_id ( ) {
return " imgui " ;
}
std : : string ImGuiUIBackend : : get_name ( ) {
return " Dear ImGui frontend " ;
}
2024-04-09 10:15:05 -07:00
void ImGuiUIBackend : : QuitHandler ( ) {
if ( main_loop = = nullptr ) {
return ;
}
( ( MainLoop * ) main_loop ) - > exit_flag . store ( true ) ;
}
2024-03-23 18:41:26 -07:00
// Main code
int ImGuiUIBackend : : run ( std : : vector < std : : string > realArgs , int argc , char * * argv )
{
2024-12-19 14:00:34 -08:00
SDL_setenv ( " SDL_VIDEO_X11_WMCLASS " , " looper " ) ;
2024-03-23 18:41:26 -07:00
int possible_error = UIBackend : : run ( realArgs , argc , argv ) ;
if ( possible_error ! = 0 ) {
return possible_error ;
}
2024-05-02 14:52:11 -07:00
MainLoop * loop = new MainLoop ( ) ;
loop - > playback = playback ;
loop - > args = args ;
main_loop = loop ;
return loop - > Run ( ) ;
2024-03-26 18:39:02 -07:00
}
void ImGuiUIBackend : : add_licenses ( ) {
auto & license_data = get_license_data ( ) ;
auto glib = LicenseData ( " Glib " , " lgpl-2.1-or-later " ) ;
auto imgui = LicenseData ( " Dear ImGui " , " MIT " ) ;
auto libintl = LicenseData ( " libintl " , " LGPL-2.1-only " ) ;
auto imfb = LicenseData ( " imgui-filebrowser " , " MIT " ) ;
auto noto = LicenseData ( " Noto Sans " , " OFL-1.1-RFN " ) ;
auto noto_jp = LicenseData ( " Noto Sans JP " , " OFL-1.1-RFN " ) ;
auto fork_awesome = LicenseData ( " Fork Awesome " , " OFL-1.1-RFN " ) ;
auto ifch = LicenseData ( " IconFontCppHeaders " , " Zlib " ) ;
LOAD_LICENSE ( glib , lgpl_2_1 ) ;
LOAD_LICENSE ( imgui , imgui ) ;
LOAD_LICENSE ( libintl , lgpl_2_1 ) ;
LOAD_LICENSE ( imfb , imgui_filebrowser ) ;
LOAD_LICENSE ( noto , notosans ) ;
LOAD_LICENSE ( noto_jp , notosansjp ) ;
LOAD_LICENSE ( fork_awesome , forkawesome ) ;
LOAD_LICENSE ( ifch , icnfntcpphdrs ) ;
license_data . insert ( glib ) ;
license_data . insert ( imgui ) ;
license_data . insert ( libintl ) ;
license_data . insert ( imfb ) ;
license_data . insert ( noto ) ;
license_data . insert ( noto_jp ) ;
license_data . insert ( fork_awesome ) ;
license_data . insert ( ifch ) ;
# ifdef PORTALS
auto libportal = LicenseData ( " libportal " , " LGPL-3.0-only " ) ;
LOAD_LICENSE ( libportal , lgpl_3_0 ) ;
license_data . insert ( libportal ) ;
# endif
2024-04-10 13:25:18 -07:00
}