mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-06-04 20:46:29 +03:00
Single instance support (tested in Linux and Windows, pending the rest of the OSes, issue SpartanJ/ecode#58).
Refresh buttons state when Clear Menu is used (issue SpartanJ/ecode#339).
This commit is contained in:
@@ -124,6 +124,12 @@ class EE_API Sys {
|
||||
|
||||
/** @return The process environment variables */
|
||||
static std::unordered_map<std::string, std::string> getEnvironmentVariables();
|
||||
|
||||
/** @return The process ids found with the correspoding process / binary / executable name */
|
||||
static std::vector<Uint64> pidof( const std::string& processName );
|
||||
|
||||
/** @returns The unix timestamp of the process creation time */
|
||||
static Uint64 getProcessCreationTime( Uint64 pid );
|
||||
};
|
||||
|
||||
}} // namespace EE::System
|
||||
|
||||
@@ -105,7 +105,7 @@ enum UINodeType {
|
||||
UI_TYPE_STACK_LAYOUT,
|
||||
UI_TYPE_MODULES = 10000,
|
||||
UI_TYPE_TERMINAL = 10001,
|
||||
UI_TYPE_USER = 100000
|
||||
UI_TYPE_USER = 200000
|
||||
};
|
||||
|
||||
enum class ScrollBarMode : Uint32 { Auto, AlwaysOn, AlwaysOff };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 14.0.1, 2024-09-21T01:28:27. -->
|
||||
<!-- Written by QtCreator 14.0.1, 2024-09-28T00:59:35. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#include <cerrno>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <ctype.h>
|
||||
#include <eepp/core/string.hpp>
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
#include <eepp/system/log.hpp>
|
||||
#include <eepp/system/sys.hpp>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
@@ -15,8 +18,10 @@
|
||||
#endif
|
||||
|
||||
#if defined( EE_PLATFORM_POSIX )
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_MACOS
|
||||
@@ -31,12 +36,24 @@
|
||||
#include <windows.h>
|
||||
#undef GetDiskFreeSpace
|
||||
#undef GetTempPath
|
||||
|
||||
// clang-format off
|
||||
#include <psapi.h>
|
||||
#include <tlhelp32.h>
|
||||
// clang-format on
|
||||
|
||||
// Dynamically load PSAPI functions for Windows
|
||||
typedef BOOL( WINAPI* EnumProcesses_t )( DWORD*, DWORD, DWORD* );
|
||||
typedef BOOL( WINAPI* EnumProcessModules_t )( HANDLE, HMODULE*, DWORD, LPDWORD );
|
||||
typedef DWORD( WINAPI* GetModuleBaseName_t )( HANDLE, HMODULE, LPSTR, DWORD );
|
||||
|
||||
#elif EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_ANDROID
|
||||
#include <libgen.h>
|
||||
#include <mntent.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#elif EE_PLATFORM == EE_PLATFORM_HAIKU
|
||||
#include <Directory.h>
|
||||
#include <OS.h>
|
||||
#include <Path.h>
|
||||
#include <Volume.h>
|
||||
#include <VolumeRoster.h>
|
||||
@@ -47,10 +64,13 @@
|
||||
#elif EE_PLATFORM == EE_PLATFORM_SOLARIS
|
||||
#include <stdlib.h>
|
||||
#elif EE_PLATFORM == EE_PLATFORM_BSD
|
||||
#include <unistd.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_MACOS || EE_PLATFORM == EE_PLATFORM_IOS
|
||||
#include <libproc.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <spawn.h>
|
||||
#endif
|
||||
@@ -1268,7 +1288,9 @@ std::string Sys::getProcessFilePath() {
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
std::wstring exename( _MAX_DIR, 0 );
|
||||
GetModuleFileNameW( 0, &exename[0], _MAX_PATH );
|
||||
DWORD size = GetModuleFileNameW( 0, &exename[0], _MAX_PATH );
|
||||
if ( size > 0 && size < _MAX_PATH )
|
||||
exename.resize( size ); // Resize to actual size without extra null characters
|
||||
return String( exename ).toUtf8();
|
||||
#elif EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_ANDROID
|
||||
char path[] = "/proc/self/exe";
|
||||
@@ -1300,4 +1322,243 @@ std::string Sys::getProcessFilePath() {
|
||||
#endif
|
||||
}
|
||||
|
||||
Uint64 Sys::getProcessCreationTime( Uint64 pid ) {
|
||||
Uint64 creationTime = 0;
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
int rpid = static_cast<int>( pid );
|
||||
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, rpid );
|
||||
if ( hProcess == NULL ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILETIME creationFileTime, exitFileTime, kernelFileTime, userFileTime;
|
||||
if ( GetProcessTimes( hProcess, &creationFileTime, &exitFileTime, &kernelFileTime,
|
||||
&userFileTime ) ) {
|
||||
ULARGE_INTEGER ull;
|
||||
ull.LowPart = creationFileTime.dwLowDateTime;
|
||||
ull.HighPart = creationFileTime.dwHighDateTime;
|
||||
|
||||
// Convert from Windows file time to Unix timestamp
|
||||
creationTime =
|
||||
static_cast<time_t>( ( ull.QuadPart - 116444736000000000ULL ) / 10000000ULL );
|
||||
} else {
|
||||
creationTime = -1;
|
||||
}
|
||||
|
||||
CloseHandle( hProcess );
|
||||
|
||||
#elif EE_PLATFORM == EE_PLATFORM_LINUX
|
||||
std::ifstream statFile( "/proc/" + std::to_string( pid ) + "/stat" );
|
||||
if ( !statFile.is_open() ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string token;
|
||||
long startTimeTicks = 0;
|
||||
int field = 1;
|
||||
while ( statFile >> token ) {
|
||||
if ( field == 22 ) { // The 22nd field is the start time in clock ticks
|
||||
startTimeTicks = std::stol( token );
|
||||
break;
|
||||
}
|
||||
field++;
|
||||
}
|
||||
|
||||
struct sysinfo sysInfo;
|
||||
sysinfo( &sysInfo );
|
||||
long uptime = sysInfo.uptime;
|
||||
|
||||
long clockTicksPerSecond = sysconf( _SC_CLK_TCK );
|
||||
creationTime = time( NULL ) - uptime + ( startTimeTicks / clockTicksPerSecond );
|
||||
|
||||
statFile.close();
|
||||
|
||||
#elif EE_PLATFORM == EE_PLATFORM_MACOS
|
||||
struct proc_bsdinfo procInfo;
|
||||
int rpid = static_cast<int>( pid );
|
||||
int status = proc_pidinfo( rpid, PROC_PIDTBSDINFO, 0, &procInfo, sizeof( procInfo ) );
|
||||
if ( status <= 0 ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
creationTime = procInfo.pbi_start_tvsec;
|
||||
|
||||
#elif EE_PLATFORM == EE_PLATFORM_BSD
|
||||
struct kinfo_proc proc;
|
||||
int rpid = static_cast<int>( pid );
|
||||
size_t procLen = sizeof( proc );
|
||||
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, rpid };
|
||||
|
||||
if ( sysctl( mib, 4, &proc, &procLen, NULL, 0 ) < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
creationTime = proc.ki_start.tv_sec;
|
||||
|
||||
#elif EE_PLATFORM == EE_PLATFORM_HAIKU
|
||||
thread_info threadInfo;
|
||||
int rpid = static_cast<int>( pid );
|
||||
status_t result = get_thread_info( rpid, &threadInfo ); // Get thread info for the PID passed
|
||||
if ( result == B_OK ) {
|
||||
// Approximate creation time by subtracting CPU time (user_time + kernel_time) from current
|
||||
// time
|
||||
creationTime = time( NULL ) - ( threadInfo.user_time + threadInfo.kernel_time ) /
|
||||
1000000; // Convert microseconds to seconds
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
std::vector<Uint64> Sys::pidof( const std::string& processName ) {
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
std::vector<Uint64> pids;
|
||||
std::vector<std::string> extensions = getEnvSplitted( "PATHEXT" );
|
||||
|
||||
HMODULE hPsapi = LoadLibrary( TEXT( "psapi.dll" ) );
|
||||
if ( !hPsapi )
|
||||
return pids;
|
||||
|
||||
EnumProcesses_t EnumProcesses = (EnumProcesses_t)GetProcAddress( hPsapi, "EnumProcesses" );
|
||||
EnumProcessModules_t EnumProcessModules =
|
||||
(EnumProcessModules_t)GetProcAddress( hPsapi, "EnumProcessModules" );
|
||||
GetModuleBaseName_t GetModuleBaseName =
|
||||
(GetModuleBaseName_t)GetProcAddress( hPsapi, "GetModuleBaseNameA" );
|
||||
|
||||
if ( !EnumProcesses || !EnumProcessModules || !GetModuleBaseName ) {
|
||||
FreeLibrary( hPsapi );
|
||||
eePRINTL( "EnumProcesses or EnumProcessModules or GetModuleBaseName failed" );
|
||||
return pids;
|
||||
}
|
||||
|
||||
DWORD processIds[1024], cbNeeded;
|
||||
if ( !EnumProcesses( processIds, sizeof( processIds ), &cbNeeded ) ) {
|
||||
FreeLibrary( hPsapi );
|
||||
eePRINTL( "EnumProcesses failed" );
|
||||
return pids;
|
||||
}
|
||||
|
||||
DWORD numProcesses = cbNeeded / sizeof( DWORD );
|
||||
|
||||
for ( DWORD i = 0; i < numProcesses; ++i ) {
|
||||
if ( processIds[i] == 0 )
|
||||
continue;
|
||||
|
||||
HANDLE hProcess =
|
||||
OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processIds[i] );
|
||||
if ( hProcess ) {
|
||||
HMODULE hMod;
|
||||
DWORD cbNeededMod;
|
||||
if ( EnumProcessModules( hProcess, &hMod, sizeof( hMod ), &cbNeededMod ) ) {
|
||||
char szProcessName[MAX_PATH];
|
||||
if ( GetModuleBaseName( hProcess, hMod, szProcessName,
|
||||
sizeof( szProcessName ) / sizeof( char ) ) ) {
|
||||
std::string actualName( szProcessName, std::strlen( szProcessName ) );
|
||||
|
||||
// Check if the process name matches the input name with or without extensions
|
||||
if ( actualName == processName ) {
|
||||
pids.push_back( processIds[i] );
|
||||
} else {
|
||||
for ( const auto& ext : extensions ) {
|
||||
std::string extName = processName + ext;
|
||||
if ( actualName == extName ) {
|
||||
pids.push_back( processIds[i] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CloseHandle( hProcess );
|
||||
}
|
||||
}
|
||||
|
||||
for ( auto pid : pids )
|
||||
eePRINTL( "Found pid %d", pid );
|
||||
|
||||
FreeLibrary( hPsapi );
|
||||
return pids;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_ANDROID || \
|
||||
EE_PLATFORM == EE_PLATFORM_MACOS
|
||||
std::vector<Uint64> pids;
|
||||
DIR* dir = opendir( "/proc" );
|
||||
if ( !dir ) {
|
||||
return pids;
|
||||
}
|
||||
|
||||
struct dirent* entry;
|
||||
while ( ( entry = readdir( dir ) ) != NULL ) {
|
||||
if ( entry->d_type == DT_DIR && isdigit( entry->d_name[0] ) ) {
|
||||
std::string pidDir = "/proc/" + std::string( entry->d_name );
|
||||
std::string cmdPath = pidDir + "/comm";
|
||||
FILE* cmdFile = fopen( cmdPath.c_str(), "r" );
|
||||
if ( cmdFile ) {
|
||||
char cmdline[256];
|
||||
if ( fgets( cmdline, sizeof( cmdline ), cmdFile ) != NULL ) {
|
||||
cmdline[strcspn( cmdline, "\n" )] = 0; // Remove newline
|
||||
if ( processName == cmdline ) {
|
||||
pids.push_back( atoi( entry->d_name ) );
|
||||
}
|
||||
}
|
||||
fclose( cmdFile );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir( dir );
|
||||
return pids;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_BSD
|
||||
std::vector<Uint64> pids;
|
||||
|
||||
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 };
|
||||
size_t len;
|
||||
|
||||
if ( sysctl( mib, 4, NULL, &len, NULL, 0 ) == -1 ) {
|
||||
return pids;
|
||||
}
|
||||
|
||||
struct kinfo_proc* procs = (struct kinfo_proc*)malloc( len );
|
||||
if ( !procs ) {
|
||||
return pids;
|
||||
}
|
||||
|
||||
if ( sysctl( mib, 4, procs, &len, NULL, 0 ) == -1 ) {
|
||||
free( procs );
|
||||
return pids;
|
||||
}
|
||||
|
||||
int proc_count = len / sizeof( struct kinfo_proc );
|
||||
|
||||
for ( int i = 0; i < proc_count; i++ ) {
|
||||
std::string name( procs[i].ki_comm );
|
||||
if ( name == processName ) {
|
||||
pids.push_back( procs[i].ki_pid );
|
||||
}
|
||||
}
|
||||
|
||||
free( procs );
|
||||
return pids;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_HAIKU
|
||||
std::vector<Uint64> pids;
|
||||
team_info teamInfo;
|
||||
int32 cookie = 0;
|
||||
std::string lProcessName = String::toLower( processName );
|
||||
|
||||
while ( get_next_team_info( &cookie, &teamInfo ) == B_OK ) {
|
||||
if ( lProcessName == String::toLower( FileSystem::fileNameFromPath( teamInfo.args ) ) ) {
|
||||
pids.push_back( teamInfo.team );
|
||||
}
|
||||
}
|
||||
|
||||
return pids;
|
||||
#else
|
||||
#warning Platform not supported
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
}} // namespace EE::System
|
||||
|
||||
@@ -117,6 +117,7 @@ void AppConfig::load( const std::string& confPath, std::string& keybindingsPath,
|
||||
ui.showStatusBar = ini.getValueB( "ui", "show_status_bar", true );
|
||||
ui.showMenuBar = ini.getValueB( "ui", "show_menu_bar", false );
|
||||
ui.welcomeScreen = ini.getValueB( "ui", "welcome_screen", true );
|
||||
ui.singleInstance = ini.getValueB( "ui", "single_instance", true );
|
||||
ui.panelPosition = panelPositionFromString( ini.getValue( "ui", "panel_position", "left" ) );
|
||||
ui.serifFont = ini.getValue( "ui", "serif_font", "fonts/NotoSans-Regular.ttf" );
|
||||
ui.monospaceFont = ini.getValue( "ui", "monospace_font", "fonts/DejaVuSansMono.ttf" );
|
||||
@@ -274,6 +275,7 @@ void AppConfig::save( const std::vector<std::string>& recentFiles,
|
||||
ini.setValueB( "ui", "show_status_bar", ui.showStatusBar );
|
||||
ini.setValueB( "ui", "show_menu_bar", ui.showMenuBar );
|
||||
ini.setValueB( "ui", "welcome_screen", ui.welcomeScreen );
|
||||
ini.setValueB( "ui", "single_instance", ui.singleInstance );
|
||||
ini.setValue( "ui", "panel_position", panelPositionToString( ui.panelPosition ) );
|
||||
ini.setValue( "ui", "serif_font", ui.serifFont );
|
||||
ini.setValue( "ui", "monospace_font", ui.monospaceFont );
|
||||
|
||||
@@ -33,6 +33,7 @@ struct UIConfig {
|
||||
bool showStatusBar{ true };
|
||||
bool showMenuBar{ false };
|
||||
bool welcomeScreen{ true };
|
||||
bool singleInstance{ true };
|
||||
PanelPosition panelPosition{ PanelPosition::Left };
|
||||
std::string serifFont;
|
||||
std::string monospaceFont;
|
||||
|
||||
@@ -529,6 +529,7 @@ bool App::loadConfig( const LogLevel& logLevel, const Sizeu& displaySize, bool s
|
||||
mThemesPath = mConfigPath + "themes";
|
||||
mScriptsPath = mConfigPath + "scripts";
|
||||
mPlaygroundPath = mConfigPath + "playground";
|
||||
mIpcPath = mConfigPath + "ipc";
|
||||
mColorSchemesPath = mConfigPath + "editor" + FileSystem::getOSSlash() + "colorschemes" +
|
||||
FileSystem::getOSSlash();
|
||||
mTerminalManager = std::make_unique<TerminalManager>( this );
|
||||
@@ -557,6 +558,16 @@ bool App::loadConfig( const LogLevel& logLevel, const Sizeu& displaySize, bool s
|
||||
FileSystem::makeDir( mPlaygroundPath );
|
||||
FileSystem::dirAddSlashAtEnd( mPlaygroundPath );
|
||||
|
||||
if ( !FileSystem::fileExists( mIpcPath ) )
|
||||
FileSystem::makeDir( mIpcPath );
|
||||
FileSystem::dirAddSlashAtEnd( mIpcPath );
|
||||
|
||||
Uint64 pid = Sys::getProcessID();
|
||||
mPidPath = mIpcPath + String::toString( pid );
|
||||
FileSystem::dirAddSlashAtEnd( mPidPath );
|
||||
if ( !FileSystem::fileExists( mPidPath ) )
|
||||
FileSystem::makeDir( mPidPath );
|
||||
|
||||
mLogsPath = mConfigPath + "ecode.log";
|
||||
|
||||
#ifndef EE_DEBUG
|
||||
@@ -754,6 +765,14 @@ App::App( const size_t& jobs, const std::vector<std::string>& args ) :
|
||||
mSettingsActions( std::make_unique<SettingsActions>( this ) ) {
|
||||
}
|
||||
|
||||
static void fsRemoveAll( const std::string& fpath ) {
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
fs::remove_all( std::filesystem::path( String( fpath ).toWideString() ) );
|
||||
#else
|
||||
fs::remove_all( fpath );
|
||||
#endif
|
||||
}
|
||||
|
||||
App::~App() {
|
||||
if ( mProjectBuildManager )
|
||||
mProjectBuildManager.reset();
|
||||
@@ -772,10 +791,26 @@ App::~App() {
|
||||
eeSAFE_DELETE( mSplitter );
|
||||
|
||||
if ( mFileSystemListener ) {
|
||||
if ( mIpcListenerId )
|
||||
mFileSystemListener->removeListener( mIpcListenerId );
|
||||
delete mFileSystemListener;
|
||||
mFileSystemListener = nullptr;
|
||||
}
|
||||
mDirTree.reset();
|
||||
|
||||
fsRemoveAll( mPidPath );
|
||||
}
|
||||
|
||||
void App::updateRecentButtons() {
|
||||
updateOpenRecentFolderBtn();
|
||||
|
||||
if ( mSplitter ) {
|
||||
mSplitter->forEachWidgetType(
|
||||
static_cast<UINodeType>( CustomWidgets::UI_TYPE_WELCOME_TAB ), []( UIWidget* widget ) {
|
||||
UIWelcomeScreen* welcomeTab = static_cast<UIWelcomeScreen*>( widget );
|
||||
welcomeTab->refresh();
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
void App::updateRecentFiles() {
|
||||
@@ -807,6 +842,7 @@ void App::updateRecentFiles() {
|
||||
} else if ( id == "clear-menu" ) {
|
||||
mRecentFiles.clear();
|
||||
updateRecentFiles();
|
||||
updateRecentButtons();
|
||||
} else {
|
||||
const String& txt = event->getNode()->asType<UIMenuItem>()->getText();
|
||||
std::string path( txt.toUtf8() );
|
||||
@@ -847,6 +883,7 @@ void App::updateRecentFolders() {
|
||||
if ( id == "clear-menu" ) {
|
||||
mRecentFolders.clear();
|
||||
updateRecentFolders();
|
||||
updateRecentButtons();
|
||||
} else if ( id == "restore-last-session-at-startup" ) {
|
||||
mConfig.workspace.restoreLastSession =
|
||||
event->getNode()->asType<UIMenuCheckBox>()->isActive();
|
||||
@@ -1575,51 +1612,50 @@ std::map<KeyBindings::Shortcut, std::string> App::getDefaultKeybindings() {
|
||||
std::map<KeyBindings::Shortcut, std::string> App::getLocalKeybindings() {
|
||||
return {
|
||||
{ { KEY_RETURN, KEYMOD_LALT | KEYMOD_LCTRL }, "fullscreen-toggle" },
|
||||
{ { KEY_F3, KEYMOD_NONE }, "repeat-find" }, { { KEY_F3, KEYMOD_SHIFT }, "find-prev" },
|
||||
{ { KEY_F12, KEYMOD_NONE }, "console-toggle" },
|
||||
{ { KEY_F, KeyMod::getDefaultModifier() }, "find-replace" },
|
||||
{ { KEY_Q, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "close-app" },
|
||||
{ { KEY_O, KeyMod::getDefaultModifier() }, "open-file" },
|
||||
{ { KEY_W, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "download-file-web" },
|
||||
{ { KEY_O, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "open-folder" },
|
||||
{ { KEY_F11, KEYMOD_NONE }, "debug-widget-tree-view" },
|
||||
{ { KEY_K, KeyMod::getDefaultModifier() }, "open-locatebar" },
|
||||
{ { KEY_P, KeyMod::getDefaultModifier() }, "open-command-palette" },
|
||||
{ { KEY_F, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "open-global-search" },
|
||||
{ { KEY_L, KeyMod::getDefaultModifier() }, "go-to-line" },
|
||||
{ { KEY_F3, KEYMOD_NONE }, "repeat-find" },
|
||||
{ { KEY_F3, KEYMOD_SHIFT }, "find-prev" },
|
||||
{ { KEY_F12, KEYMOD_NONE }, "console-toggle" },
|
||||
{ { KEY_F, KeyMod::getDefaultModifier() }, "find-replace" },
|
||||
{ { KEY_Q, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "close-app" },
|
||||
{ { KEY_O, KeyMod::getDefaultModifier() }, "open-file" },
|
||||
{ { KEY_W, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "download-file-web" },
|
||||
{ { KEY_O, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "open-folder" },
|
||||
{ { KEY_F11, KEYMOD_NONE }, "debug-widget-tree-view" },
|
||||
{ { KEY_K, KeyMod::getDefaultModifier() }, "open-locatebar" },
|
||||
{ { KEY_P, KeyMod::getDefaultModifier() }, "open-command-palette" },
|
||||
{ { KEY_F, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "open-global-search" },
|
||||
{ { KEY_L, KeyMod::getDefaultModifier() }, "go-to-line" },
|
||||
#if EE_PLATFORM == EE_PLATFORM_MACOS
|
||||
{ { KEY_M, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "menu-toggle" },
|
||||
{ { KEY_M, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "menu-toggle" },
|
||||
#else
|
||||
{ { KEY_M, KeyMod::getDefaultModifier() }, "menu-toggle" },
|
||||
{ { KEY_M, KeyMod::getDefaultModifier() }, "menu-toggle" },
|
||||
#endif
|
||||
{ { KEY_S, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "save-all" },
|
||||
{ { KEY_F9, KEYMOD_LALT }, "switch-side-panel" },
|
||||
{ { KEY_J, KeyMod::getDefaultModifier() | KEYMOD_LALT | KEYMOD_SHIFT },
|
||||
"terminal-split-left" },
|
||||
{ { KEY_L, KeyMod::getDefaultModifier() | KEYMOD_LALT | KEYMOD_SHIFT },
|
||||
"terminal-split-right" },
|
||||
{ { KEY_I, KeyMod::getDefaultModifier() | KEYMOD_LALT | KEYMOD_SHIFT },
|
||||
"terminal-split-top" },
|
||||
{ { KEY_K, KeyMod::getDefaultModifier() | KEYMOD_LALT | KEYMOD_SHIFT },
|
||||
"terminal-split-bottom" },
|
||||
{ { KEY_S, KeyMod::getDefaultModifier() | KEYMOD_LALT | KEYMOD_SHIFT },
|
||||
"terminal-split-swap" },
|
||||
{ { KEY_T, KeyMod::getDefaultModifier() | KEYMOD_LALT | KEYMOD_SHIFT },
|
||||
"reopen-closed-tab" },
|
||||
{ { KEY_1, KEYMOD_LALT }, "toggle-status-locate-bar" },
|
||||
{ { KEY_2, KEYMOD_LALT }, "toggle-status-global-search-bar" },
|
||||
{ { KEY_3, KEYMOD_LALT }, "toggle-status-terminal" },
|
||||
{ { KEY_4, KEYMOD_LALT }, "toggle-status-build-output" },
|
||||
{ { KEY_5, KEYMOD_LALT }, "toggle-status-app-output" },
|
||||
{ { KEY_B, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "project-build-start" },
|
||||
{ { KEY_C, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "project-build-cancel" },
|
||||
{ { KEY_F5, KEYMOD_NONE }, "project-build-and-run" },
|
||||
{ { KEY_O, KEYMOD_LALT | KEYMOD_SHIFT }, "show-open-documents" },
|
||||
{ { KEY_K, KeyMod::getDefaultModifier() | KEYMOD_SHIFT },
|
||||
"open-workspace-symbol-search" },
|
||||
{ { KEY_P, KeyMod::getDefaultModifier() | KEYMOD_SHIFT },
|
||||
"open-document-symbol-search" },
|
||||
{ { KEY_N, KEYMOD_SHIFT | KEYMOD_LALT }, "create-new-window" },
|
||||
{ { KEY_S, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "save-all" },
|
||||
{ { KEY_F9, KEYMOD_LALT }, "switch-side-panel" },
|
||||
{ { KEY_J, KeyMod::getDefaultModifier() | KEYMOD_LALT | KEYMOD_SHIFT },
|
||||
"terminal-split-left" },
|
||||
{ { KEY_L, KeyMod::getDefaultModifier() | KEYMOD_LALT | KEYMOD_SHIFT },
|
||||
"terminal-split-right" },
|
||||
{ { KEY_I, KeyMod::getDefaultModifier() | KEYMOD_LALT | KEYMOD_SHIFT },
|
||||
"terminal-split-top" },
|
||||
{ { KEY_K, KeyMod::getDefaultModifier() | KEYMOD_LALT | KEYMOD_SHIFT },
|
||||
"terminal-split-bottom" },
|
||||
{ { KEY_S, KeyMod::getDefaultModifier() | KEYMOD_LALT | KEYMOD_SHIFT },
|
||||
"terminal-split-swap" },
|
||||
{ { KEY_T, KeyMod::getDefaultModifier() | KEYMOD_LALT | KEYMOD_SHIFT },
|
||||
"reopen-closed-tab" },
|
||||
{ { KEY_1, KEYMOD_LALT }, "toggle-status-locate-bar" },
|
||||
{ { KEY_2, KEYMOD_LALT }, "toggle-status-global-search-bar" },
|
||||
{ { KEY_3, KEYMOD_LALT }, "toggle-status-terminal" },
|
||||
{ { KEY_4, KEYMOD_LALT }, "toggle-status-build-output" },
|
||||
{ { KEY_5, KEYMOD_LALT }, "toggle-status-app-output" },
|
||||
{ { KEY_B, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "project-build-start" },
|
||||
{ { KEY_C, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "project-build-cancel" },
|
||||
{ { KEY_F5, KEYMOD_NONE }, "project-build-and-run" },
|
||||
{ { KEY_O, KEYMOD_LALT | KEYMOD_SHIFT }, "show-open-documents" },
|
||||
{ { KEY_K, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "open-workspace-symbol-search" },
|
||||
{ { KEY_P, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "open-document-symbol-search" },
|
||||
{ { KEY_N, KEYMOD_SHIFT | KEYMOD_LALT }, "create-new-window" },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1628,15 +1664,15 @@ std::map<KeyBindings::Shortcut, std::string> App::getLocalKeybindings() {
|
||||
std::map<std::string, std::string> App::getMigrateKeybindings() {
|
||||
return {
|
||||
{ "fullscreen-toggle", "alt+return" }, { "switch-to-tab-1", "alt+1" },
|
||||
{ "switch-to-tab-2", "alt+2" }, { "switch-to-tab-3", "alt+3" },
|
||||
{ "switch-to-tab-4", "alt+4" }, { "switch-to-tab-5", "alt+5" },
|
||||
{ "switch-to-tab-6", "alt+6" }, { "switch-to-tab-7", "alt+7" },
|
||||
{ "switch-to-tab-8", "alt+8" }, { "switch-to-tab-9", "alt+9" },
|
||||
{ "switch-to-last-tab", "alt+0" },
|
||||
{ "switch-to-tab-2", "alt+2" }, { "switch-to-tab-3", "alt+3" },
|
||||
{ "switch-to-tab-4", "alt+4" }, { "switch-to-tab-5", "alt+5" },
|
||||
{ "switch-to-tab-6", "alt+6" }, { "switch-to-tab-7", "alt+7" },
|
||||
{ "switch-to-tab-8", "alt+8" }, { "switch-to-tab-9", "alt+9" },
|
||||
{ "switch-to-last-tab", "alt+0" },
|
||||
#if EE_PLATFORM == EE_PLATFORM_MACOS
|
||||
{ "menu-toggle", "mod+shift+m" },
|
||||
{ "menu-toggle", "mod+shift+m" },
|
||||
#endif
|
||||
{ "lock-toggle", "mod+shift+l" },
|
||||
{ "lock-toggle", "mod+shift+l" },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2657,6 +2693,20 @@ void App::consoleToggle() {
|
||||
mSplitter->getCurWidget()->setFocus();
|
||||
}
|
||||
|
||||
std::function<void( UICodeEditor* codeEditor, const std::string& path )>
|
||||
App::getForcePositionFn( TextPosition initialPosition ) {
|
||||
std::function<void( UICodeEditor * codeEditor, const std::string& path )> forcePosition;
|
||||
if ( initialPosition.isValid() ) {
|
||||
forcePosition = [this, initialPosition]( UICodeEditor* editor, const auto& ) {
|
||||
editor->runOnMainThread( [this, initialPosition, editor] {
|
||||
editor->goToLine( initialPosition );
|
||||
mSplitter->addEditorPositionToNavigationHistory( editor );
|
||||
} );
|
||||
};
|
||||
}
|
||||
return forcePosition;
|
||||
}
|
||||
|
||||
void App::initProjectTreeView( std::string path, bool openClean ) {
|
||||
mProjectViewEmptyCont = mUISceneNode->find<UILinearLayout>( "project_view_empty" );
|
||||
mProjectViewEmptyCont->find<UIPushButton>( "open_folder" )
|
||||
@@ -2771,16 +2821,7 @@ void App::initProjectTreeView( std::string path, bool openClean ) {
|
||||
if ( mFileSystemListener )
|
||||
mFileSystemListener->setFileSystemModel( mFileSystemModel );
|
||||
|
||||
std::function<void( UICodeEditor * codeEditor, const std::string& path )>
|
||||
forcePosition;
|
||||
if ( initialPosition.isValid() ) {
|
||||
forcePosition = [this, initialPosition]( UICodeEditor* editor, const auto& ) {
|
||||
editor->runOnMainThread( [this, initialPosition, editor] {
|
||||
editor->goToLine( initialPosition );
|
||||
mSplitter->addEditorPositionToNavigationHistory( editor );
|
||||
} );
|
||||
};
|
||||
}
|
||||
auto forcePosition = getForcePositionFn( initialPosition );
|
||||
|
||||
if ( FileSystem::fileExists( rpath ) ) {
|
||||
loadFileFromPath( rpath, false, nullptr, forcePosition );
|
||||
@@ -3040,6 +3081,56 @@ FontTrueType* App::loadFont( const std::string& name, std::string fontPath,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool App::needsRedirectToRunningProcess( std::string file ) {
|
||||
if ( !mConfig.ui.singleInstance || file.empty() )
|
||||
return false;
|
||||
|
||||
bool hasPosition = pathHasPosition( file );
|
||||
TextPosition position;
|
||||
if ( hasPosition ) {
|
||||
auto pathAndPosition = getPathAndPosition( file );
|
||||
file = pathAndPosition.first;
|
||||
position = pathAndPosition.second;
|
||||
}
|
||||
|
||||
std::string rpath( FileSystem::getRealPath( file ) );
|
||||
FileInfo finfo( rpath );
|
||||
|
||||
if ( !finfo.exists() || finfo.isDirectory() )
|
||||
return false;
|
||||
|
||||
std::string processName( FileSystem::fileNameFromPath( Sys::getProcessFilePath() ) );
|
||||
auto pids = Sys::pidof( processName );
|
||||
if ( pids.size() <= 1 )
|
||||
return false;
|
||||
|
||||
Uint64 processPid = Sys::getProcessID();
|
||||
Uint64 latestPid = processPid;
|
||||
Uint64 lastCreationTime = 0;
|
||||
|
||||
for ( const auto pid : pids ) {
|
||||
if ( pid != Sys::getProcessID() ) {
|
||||
Uint64 creationTime = Sys::getProcessCreationTime( pid );
|
||||
if ( creationTime >= lastCreationTime ) {
|
||||
latestPid = pid;
|
||||
lastCreationTime = creationTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( latestPid == processPid )
|
||||
return false;
|
||||
|
||||
std::string pidPath = mIpcPath + String::toString( latestPid );
|
||||
if ( !FileSystem::isDirectory( pidPath ) )
|
||||
return false;
|
||||
FileSystem::dirAddSlashAtEnd( pidPath );
|
||||
FileSystem::fileWrite( pidPath + MD5::fromString( finfo.getFilepath() ).toHexString(),
|
||||
finfo.getFilepath() +
|
||||
( position.isValid() ? position.toPositionString() : "" ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDensity,
|
||||
const std::string& colorScheme, bool terminal, bool frameBuffer, bool benchmarkMode,
|
||||
const std::string& css, bool health, const std::string& healthLang,
|
||||
@@ -3073,6 +3164,9 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe
|
||||
return;
|
||||
}
|
||||
|
||||
if ( needsRedirectToRunningProcess( file ) )
|
||||
return;
|
||||
|
||||
currentDisplay = displayManager->getDisplayIndex( mConfig.windowState.displayIndex <
|
||||
displayManager->getDisplayCount()
|
||||
? mConfig.windowState.displayIndex
|
||||
@@ -3437,8 +3531,37 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe
|
||||
mFileWatcher = new efsw::FileWatcher();
|
||||
mFileSystemListener = new FileSystemListener( mSplitter, mFileSystemModel, { mLogsPath } );
|
||||
mFileWatcher->addWatch( mPluginsPath, mFileSystemListener );
|
||||
mFileWatcher->addWatch( mPidPath, mFileSystemListener );
|
||||
mFileWatcher->watch();
|
||||
mPluginManager->setFileSystemListener( mFileSystemListener );
|
||||
mIpcListenerId = mFileSystemListener->addListener( [this]( const FileEvent& fe,
|
||||
const FileInfo& fi ) {
|
||||
if ( !( ( fe.type == FileSystemEventType::Add ||
|
||||
fe.type == FileSystemEventType::Modified ) &&
|
||||
fe.directory == mPidPath ) )
|
||||
return;
|
||||
std::string path;
|
||||
FileSystem::fileGet( fi.getFilepath(), path );
|
||||
String::trimInPlace( path, ' ' );
|
||||
String::trimInPlace( path, '\n' );
|
||||
|
||||
bool hasPosition = pathHasPosition( path );
|
||||
TextPosition initialPosition;
|
||||
if ( hasPosition ) {
|
||||
auto pathAndPosition = getPathAndPosition( path );
|
||||
path = pathAndPosition.first;
|
||||
initialPosition = pathAndPosition.second;
|
||||
}
|
||||
|
||||
if ( FileSystem::fileExists( path ) ) {
|
||||
mUISceneNode->runOnMainThread( [path, initialPosition, this] {
|
||||
loadFileFromPath( path, true, nullptr, getForcePositionFn( initialPosition ) );
|
||||
} );
|
||||
if ( !mWindow->hasFocus() )
|
||||
mWindow->raise();
|
||||
}
|
||||
FileSystem::fileRemove( fi.getFilepath() );
|
||||
} );
|
||||
#endif
|
||||
|
||||
mNotificationCenter = std::make_unique<NotificationCenter>(
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
|
||||
using namespace eterm::UI;
|
||||
|
||||
enum class CustomWidgets {
|
||||
UI_TYPE_WELCOME_TAB = UI_TYPE_USER + 1,
|
||||
};
|
||||
|
||||
namespace ecode {
|
||||
|
||||
class AutoCompletePlugin;
|
||||
@@ -332,6 +336,8 @@ class App : public UICodeEditorSplitter::Client {
|
||||
|
||||
void updateRecentFolders();
|
||||
|
||||
void updateRecentButtons();
|
||||
|
||||
const CodeEditorConfig& getCodeEditorConfig() const;
|
||||
|
||||
AppConfig& getConfig();
|
||||
@@ -495,6 +501,8 @@ class App : public UICodeEditorSplitter::Client {
|
||||
std::string mi18nPath;
|
||||
std::string mScriptsPath;
|
||||
std::string mPlaygroundPath;
|
||||
std::string mIpcPath;
|
||||
std::string mPidPath;
|
||||
Float mDisplayDPI{ 96 };
|
||||
std::shared_ptr<ThreadPool> mThreadPool;
|
||||
std::shared_ptr<ProjectDirectoryTree> mDirTree;
|
||||
@@ -546,6 +554,7 @@ class App : public UICodeEditorSplitter::Client {
|
||||
UIMenuBar* mMenuBar{ nullptr };
|
||||
std::unique_ptr<SettingsActions> mSettingsActions;
|
||||
std::vector<std::string> mPathsToLoad;
|
||||
Uint64 mIpcListenerId{ 0 };
|
||||
|
||||
void saveAllProcess();
|
||||
|
||||
@@ -647,6 +656,11 @@ class App : public UICodeEditorSplitter::Client {
|
||||
void insertRecentFileAndUpdateUI( const std::string& path );
|
||||
|
||||
void createWelcomeTab();
|
||||
|
||||
bool needsRedirectToRunningProcess( std::string file );
|
||||
|
||||
std::function<void( UICodeEditor*, const std::string& )>
|
||||
getForcePositionFn( TextPosition initialPosition );
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -154,7 +154,7 @@ class AutoCompletePlugin : public Plugin {
|
||||
Text mSuggestionDoc;
|
||||
size_t mMaxLabelCharacters{ 100 };
|
||||
String::HashType mConfigHash{ 0 };
|
||||
UnorderedMap<std::string, std::string> mKeyBindings;
|
||||
std::unordered_map<std::string, std::string> mKeyBindings;
|
||||
std::unordered_map<std::string, KeyBindings::Shortcut> mShortcuts;
|
||||
|
||||
Float mRowHeight{ 0 };
|
||||
|
||||
@@ -251,25 +251,27 @@ LSPClientPlugin::~LSPClientPlugin() {
|
||||
mShuttingDown = true;
|
||||
mManager->unsubscribeMessages( this );
|
||||
unsubscribeFileSystemListener();
|
||||
Lock l( mDocMutex );
|
||||
for ( const auto& editor : mEditors ) {
|
||||
for ( auto& kb : mKeyBindings ) {
|
||||
editor.first->getKeyBindings().removeCommandKeybind( kb.first );
|
||||
if ( editor.first->hasDocument() )
|
||||
editor.first->getDocument().removeCommand( kb.first );
|
||||
{
|
||||
Lock l( mDocMutex );
|
||||
for ( const auto& editor : mEditors ) {
|
||||
for ( auto& kb : mKeyBindings ) {
|
||||
editor.first->getKeyBindings().removeCommandKeybind( kb.first );
|
||||
if ( editor.first->hasDocument() )
|
||||
editor.first->getDocument().removeCommand( kb.first );
|
||||
}
|
||||
for ( auto listener : editor.second )
|
||||
editor.first->removeEventListener( listener );
|
||||
if ( mBreadcrumb )
|
||||
editor.first->unregisterTopSpace( this );
|
||||
editor.first->unregisterPlugin( this );
|
||||
}
|
||||
for ( auto listener : editor.second )
|
||||
editor.first->removeEventListener( listener );
|
||||
if ( mBreadcrumb )
|
||||
editor.first->unregisterTopSpace( this );
|
||||
editor.first->unregisterPlugin( this );
|
||||
}
|
||||
if ( nullptr == mManager->getSplitter() )
|
||||
return;
|
||||
for ( const auto& editor : mEditorsTags ) {
|
||||
if ( mManager->getSplitter()->editorExists( editor.first ) ) {
|
||||
for ( const auto& tag : editor.second )
|
||||
editor.first->removeActionsByTag( tag );
|
||||
if ( nullptr == mManager->getSplitter() )
|
||||
return;
|
||||
for ( const auto& editor : mEditorsTags ) {
|
||||
if ( mManager->getSplitter()->editorExists( editor.first ) ) {
|
||||
for ( const auto& tag : editor.second )
|
||||
editor.first->removeActionsByTag( tag );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ class LSPClientPlugin : public Plugin {
|
||||
bool mBreadcrumb{ true };
|
||||
bool mHoveringBreadcrumb{ false };
|
||||
StyleSheetLength mBreadcrumbHeight{ "20dp" };
|
||||
UnorderedMap<std::string, std::string> mKeyBindings; /* cmd, shortcut */
|
||||
std::unordered_map<std::string, std::string> mKeyBindings; /* cmd, shortcut */
|
||||
UnorderedMap<TextDocument*, std::shared_ptr<TextDocument>> mDelayedDocs;
|
||||
Uint32 mHoverWaitCb{ 0 };
|
||||
LSPHover mCurrentHover;
|
||||
|
||||
@@ -71,6 +71,11 @@ void Plugin::showMessage( LSPMessageType type, const std::string& message,
|
||||
&msgReq );
|
||||
}
|
||||
|
||||
Plugin::~Plugin() {
|
||||
while ( mLoading )
|
||||
Sys::sleep( Milliseconds( 1 ) );
|
||||
}
|
||||
|
||||
void Plugin::onFileSystemEvent( const FileEvent& ev, const FileInfo& file ) {
|
||||
if ( ev.type != FileSystemEventType::Modified || mShuttingDown || isLoading() )
|
||||
return;
|
||||
|
||||
@@ -17,6 +17,8 @@ class Plugin : public UICodeEditorPlugin {
|
||||
public:
|
||||
explicit Plugin( PluginManager* manager );
|
||||
|
||||
virtual ~Plugin();
|
||||
|
||||
void subscribeFileSystemListener();
|
||||
|
||||
void unsubscribeFileSystemListener();
|
||||
@@ -89,23 +91,23 @@ class PluginBase : public Plugin {
|
||||
//! If the configuration is stored in a file, keep track of the config hash
|
||||
String::HashType mConfigHash{ 0 };
|
||||
|
||||
virtual void onDocumentLoaded( TextDocument* ){};
|
||||
virtual void onDocumentLoaded( TextDocument* ) {};
|
||||
|
||||
virtual void onDocumentClosed( TextDocument* ){};
|
||||
virtual void onDocumentClosed( TextDocument* ) {};
|
||||
|
||||
virtual void onDocumentChanged( UICodeEditor*, TextDocument* /*oldDoc*/ ){};
|
||||
virtual void onDocumentChanged( UICodeEditor*, TextDocument* /*oldDoc*/ ) {};
|
||||
|
||||
virtual void onRegisterListeners( UICodeEditor*, std::vector<Uint32>& /*listeners*/ ){};
|
||||
virtual void onRegisterListeners( UICodeEditor*, std::vector<Uint32>& /*listeners*/ ) {};
|
||||
|
||||
//! Usually used to remove keybindings in an editor
|
||||
virtual void onBeforeUnregister( UICodeEditor* ){};
|
||||
virtual void onBeforeUnregister( UICodeEditor* ) {};
|
||||
|
||||
virtual void onRegisterDocument( TextDocument* ){};
|
||||
virtual void onRegisterDocument( TextDocument* ) {};
|
||||
|
||||
virtual void onUnregisterEditor( UICodeEditor* ){};
|
||||
virtual void onUnregisterEditor( UICodeEditor* ) {};
|
||||
|
||||
//! Usually used to unregister commands in a document
|
||||
virtual void onUnregisterDocument( TextDocument* ){};
|
||||
virtual void onUnregisterDocument( TextDocument* ) {};
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -1169,6 +1169,14 @@ UIMenu* SettingsMenu::createWindowMenu() {
|
||||
->setId( "zoom-reset" );
|
||||
|
||||
mWindowMenu->addSeparator();
|
||||
mWindowMenu
|
||||
->addCheckBox( i18n( "single_instance_enable", "Enable Single Instance" ),
|
||||
mApp->getConfig().ui.singleInstance )
|
||||
->setTooltipText(
|
||||
i18n( "single_instance_desc",
|
||||
"Newly opened files will be opened in the latest opened ecode instance." ) )
|
||||
->setId( "single-instance-enable" );
|
||||
|
||||
mWindowMenu
|
||||
->addCheckBox( i18n( "welcome_screen_enable", "Enable Welcome Screen" ),
|
||||
mApp->getConfig().ui.welcomeScreen )
|
||||
@@ -1187,6 +1195,10 @@ UIMenu* SettingsMenu::createWindowMenu() {
|
||||
} else if ( "welcome-screen-enable" == item->getId() ) {
|
||||
bool active = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mApp->getConfig().ui.welcomeScreen = active;
|
||||
} else if ( "single-instance-enable" == item->getId() ) {
|
||||
bool active = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mApp->getConfig().ui.singleInstance = active;
|
||||
mApp->saveConfig();
|
||||
} else {
|
||||
String text = String( event->getNode()->asType<UIMenuItem>()->getId() ).toLower();
|
||||
String::replaceAll( text, " ", "-" );
|
||||
|
||||
@@ -177,11 +177,20 @@ UIWelcomeScreen* UIWelcomeScreen::New( App* app ) {
|
||||
return eeNew( UIWelcomeScreen, ( app ) );
|
||||
}
|
||||
|
||||
Uint32 UIWelcomeScreen::getType() const {
|
||||
return static_cast<Uint32>( CustomWidgets::UI_TYPE_WELCOME_TAB );
|
||||
}
|
||||
|
||||
bool UIWelcomeScreen::isType( const Uint32& type ) const {
|
||||
return UIWelcomeScreen::getType() == type ? true : UIRelativeLayout::isType( type );
|
||||
}
|
||||
|
||||
UIWelcomeScreen::UIWelcomeScreen( App* app ) :
|
||||
UIRelativeLayout(),
|
||||
WidgetCommandExecuter( getUISceneNode()->getWindow()->getInput() ),
|
||||
mApp( app ) {
|
||||
setId( "welcome_ecode" );
|
||||
addClass( "welcome_tab" );
|
||||
setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent );
|
||||
app->registerUnlockedCommands( *this );
|
||||
getUISceneNode()->loadLayoutFromString( LAYOUT, this, String::hash( "UIWelcomeScreen" ) );
|
||||
@@ -192,8 +201,7 @@ UIWelcomeScreen::UIWelcomeScreen( App* app ) :
|
||||
return;
|
||||
node->setTooltipText( getKeyBindings().getCommandKeybindString( id ) );
|
||||
node->onClick(
|
||||
[this]( const MouseEvent* event ) { mApp->runCommand( event->getNode()->getId() ); },
|
||||
EE_BUTTON_LEFT );
|
||||
[this]( const MouseEvent* event ) { mApp->runCommand( event->getNode()->getId() ); } );
|
||||
};
|
||||
|
||||
auto bindBtns = [bindBtn]( const std::initializer_list<std::string> ids ) {
|
||||
@@ -205,26 +213,14 @@ UIWelcomeScreen::UIWelcomeScreen( App* app ) :
|
||||
"check-for-updates", "plugin-manager-open", "keybindings" } );
|
||||
|
||||
auto recentFolders = find( "recent-folders" );
|
||||
if ( !mApp->getRecentFolders().empty() ) {
|
||||
recentFolders->onClick(
|
||||
[this]( const MouseEvent* event ) {
|
||||
mApp->createAndShowRecentFolderPopUpMenu( event->getNode() );
|
||||
},
|
||||
EE_BUTTON_LEFT );
|
||||
} else {
|
||||
recentFolders->setEnabled( false );
|
||||
}
|
||||
recentFolders->onClick( [this]( const MouseEvent* event ) {
|
||||
mApp->createAndShowRecentFolderPopUpMenu( event->getNode() );
|
||||
} );
|
||||
|
||||
auto recentFiles = find( "recent-files" );
|
||||
if ( !mApp->getRecentFiles().empty() ) {
|
||||
recentFiles->onClick(
|
||||
[this]( const MouseEvent* event ) {
|
||||
mApp->createAndShowRecentFilesPopUpMenu( event->getNode() );
|
||||
},
|
||||
EE_BUTTON_LEFT );
|
||||
} else {
|
||||
recentFiles->setEnabled( false );
|
||||
}
|
||||
recentFiles->onClick( [this]( const MouseEvent* event ) {
|
||||
mApp->createAndShowRecentFilesPopUpMenu( event->getNode() );
|
||||
} );
|
||||
|
||||
find<UITextView>( "main_menu_shortcut" )->setText( mApp->getKeybind( "menu-toggle" ) );
|
||||
|
||||
@@ -244,6 +240,15 @@ UIWelcomeScreen::UIWelcomeScreen( App* app ) :
|
||||
welcomeDisabledChk->on( Event::OnValueChange, [welcomeDisabledChk, this]( auto ) {
|
||||
mApp->getConfig().ui.welcomeScreen = !welcomeDisabledChk->isChecked();
|
||||
} );
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void UIWelcomeScreen::refresh() {
|
||||
auto recentFolders = find( "recent-folders" );
|
||||
auto recentFiles = find( "recent-files" );
|
||||
recentFolders->setEnabled( !mApp->getRecentFolders().empty() );
|
||||
recentFiles->setEnabled( !mApp->getRecentFiles().empty() );
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -24,6 +24,12 @@ class UIWelcomeScreen : public UIRelativeLayout, public WidgetCommandExecuter {
|
||||
return WidgetCommandExecuter::onKeyDown( event );
|
||||
}
|
||||
|
||||
void refresh();
|
||||
|
||||
Uint32 getType() const;
|
||||
|
||||
bool isType( const Uint32& type ) const;
|
||||
|
||||
protected:
|
||||
App* mApp{ nullptr };
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user