mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-08-11 17:17:42 +03:00
Warn before closing terminal that is running some process (SpartanJ/ecode#644), probably still WIP, not tested outside Linux (it might not build).
Build ecode with -g1, it should improve crash information. Regain editor focus after escaping from settings menu. Prevent crash when during widget splitting (couldn't reproduce it but this just avoid crashing, SpartanJ/ecode#650).
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
|
||||
namespace EE { namespace System {
|
||||
|
||||
using ProcessID = Uint64;
|
||||
|
||||
class EE_API Sys {
|
||||
public:
|
||||
enum class PlatformType {
|
||||
@@ -28,7 +30,7 @@ class EE_API Sys {
|
||||
};
|
||||
|
||||
/** @return The current process id */
|
||||
static Uint64 getProcessID();
|
||||
static ProcessID getProcessID();
|
||||
|
||||
/** @return the current date time */
|
||||
static std::string getDateTimeStr();
|
||||
@@ -126,19 +128,22 @@ class EE_API Sys {
|
||||
static std::unordered_map<std::string, std::string> getEnvironmentVariables();
|
||||
|
||||
/** @return The process ids found with the corresponding process / binary / executable name */
|
||||
static std::vector<Uint64> pidof( const std::string& processName );
|
||||
static std::vector<ProcessID> pidof( const std::string& processName );
|
||||
|
||||
/** @return A list of the current running processes */
|
||||
static std::vector<std::pair<Uint64, std::string>> listProcesses();
|
||||
static std::vector<std::pair<ProcessID, std::string>> listProcesses();
|
||||
|
||||
/** @returns The unix timestamp of the process creation time */
|
||||
static Int64 getProcessCreationTime( Uint64 pid );
|
||||
static Int64 getProcessCreationTime( ProcessID pid );
|
||||
|
||||
/** @returns The target destination of a windows shortcut path (.lnk files) */
|
||||
static std::string getShortcutTarget( const std::string& lnkFilePath );
|
||||
|
||||
/** @returns The user home directory */
|
||||
static std::string getUserDirectory();
|
||||
|
||||
/** @returns True if the process has any childrens */
|
||||
static bool processHasChildren( ProcessID pid );
|
||||
};
|
||||
|
||||
}} // namespace EE::System
|
||||
|
||||
@@ -365,6 +365,10 @@ class EE_API UICodeEditorSplitter {
|
||||
|
||||
std::shared_ptr<TextDocument> getTextDocumentRef( TextDocument* doc );
|
||||
|
||||
void setTabTryCloseCallback( UITabWidget::TabTryCloseCallback cb );
|
||||
|
||||
bool isEditorInAnyWidget( UICodeEditor* ) const;
|
||||
|
||||
protected:
|
||||
UISceneNode* mUISceneNode{ nullptr };
|
||||
std::shared_ptr<ThreadPool> mThreadPool;
|
||||
@@ -391,6 +395,7 @@ class EE_API UICodeEditorSplitter {
|
||||
size_t mNavigationHistoryPos{ std::numeric_limits<size_t>::max() };
|
||||
std::function<void( UITabWidget* )> mOnTabWidgetCreateCb;
|
||||
Float mVisualSplitEdgePercent{ 0.1 };
|
||||
UITabWidget::TabTryCloseCallback mTabTryCloseCb;
|
||||
|
||||
UICodeEditorSplitter( UICodeEditorSplitter::Client* client, UISceneNode* sceneNode,
|
||||
std::shared_ptr<ThreadPool> threadPool,
|
||||
|
||||
@@ -1668,7 +1668,14 @@ solution "eepp"
|
||||
build_link_configuration( "ecode", false )
|
||||
configuration { "release", "windows" }
|
||||
if not is_vs() then
|
||||
linkoptions { "-Wl,--export-all-symbols" }
|
||||
buildoptions { "-g1" }
|
||||
end
|
||||
configuration { "release" }
|
||||
if os.is_real("linux") or os.is_real("macosx") or os.is_real("bsd") or os.is_real("haiku") then
|
||||
buildoptions { "-g1", "-fvisibility=default" }
|
||||
end
|
||||
if os.is_real("linux") or os.is_real("bsd") then
|
||||
linkoptions { "-rdynamic" }
|
||||
end
|
||||
|
||||
project "eterm"
|
||||
|
||||
@@ -1507,6 +1507,10 @@ workspace "eepp"
|
||||
links { "dw" }
|
||||
defines { "ECODE_HAS_DW" }
|
||||
end
|
||||
filter { "system:linux or system:macosx or system:haiku or system:bsd", "configurations:release*" }
|
||||
buildoptions { "-g1", "-fvisibility=default" }
|
||||
filter { "system:linux or system:bsd", "configurations:release*" }
|
||||
linkoptions { "-rdynamic" }
|
||||
filter { "system:windows" }
|
||||
links { "dbghelp", "psapi" }
|
||||
filter "system:haiku"
|
||||
@@ -1514,7 +1518,7 @@ workspace "eepp"
|
||||
filter "system:bsd"
|
||||
links { "util" }
|
||||
filter { "system:windows", "action:not vs*", "configurations:release*" }
|
||||
linkoptions { "-Wl,--export-all-symbols" }
|
||||
buildoptions { "-g1" }
|
||||
|
||||
project "eterm"
|
||||
set_kind()
|
||||
|
||||
@@ -700,7 +700,7 @@ double Sys::getSystemTime() {
|
||||
#endif
|
||||
}
|
||||
|
||||
Uint64 Sys::getProcessID() {
|
||||
ProcessID Sys::getProcessID() {
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
return GetCurrentProcessId();
|
||||
#elif EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN
|
||||
@@ -1181,9 +1181,9 @@ std::vector<std::string> Sys::getEnvSplit( const std::string& name ) {
|
||||
static ULONG_PTR GetParentProcessId() {
|
||||
ULONG_PTR pbi[6];
|
||||
ULONG ulSize = 0;
|
||||
LONG( WINAPI * NtQueryInformationProcess )
|
||||
( HANDLE ProcessHandle, ULONG ProcessInformationClass, PVOID ProcessInformation,
|
||||
ULONG ProcessInformationLength, PULONG ReturnLength );
|
||||
LONG( WINAPI * NtQueryInformationProcess )(
|
||||
HANDLE ProcessHandle, ULONG ProcessInformationClass, PVOID ProcessInformation,
|
||||
ULONG ProcessInformationLength, PULONG ReturnLength );
|
||||
*(FARPROC*)&NtQueryInformationProcess =
|
||||
GetProcAddress( LoadLibraryA( "NTDLL.DLL" ), "NtQueryInformationProcess" );
|
||||
if ( NtQueryInformationProcess ) {
|
||||
@@ -1338,7 +1338,7 @@ std::string Sys::getProcessFilePath() {
|
||||
#endif
|
||||
}
|
||||
|
||||
Int64 Sys::getProcessCreationTime( Uint64 pid ) {
|
||||
Int64 Sys::getProcessCreationTime( ProcessID pid ) {
|
||||
Int64 creationTime = -1;
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
@@ -1426,9 +1426,9 @@ Int64 Sys::getProcessCreationTime( Uint64 pid ) {
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
std::vector<Uint64> Sys::pidof( const std::string& processName ) {
|
||||
std::vector<ProcessID> Sys::pidof( const std::string& processName ) {
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
std::vector<Uint64> pids;
|
||||
std::vector<ProcessID> pids;
|
||||
std::vector<std::string> extensions = getEnvSplit( "PATHEXT" );
|
||||
|
||||
HMODULE hPsapi = LoadLibrary( TEXT( "psapi.dll" ) );
|
||||
@@ -1496,7 +1496,7 @@ std::vector<Uint64> Sys::pidof( const std::string& processName ) {
|
||||
return pids;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_ANDROID
|
||||
|
||||
std::vector<Uint64> pids;
|
||||
std::vector<ProcessID> pids;
|
||||
DIR* dir = opendir( "/proc" );
|
||||
if ( !dir ) {
|
||||
return pids;
|
||||
@@ -1524,7 +1524,7 @@ std::vector<Uint64> Sys::pidof( const std::string& processName ) {
|
||||
closedir( dir );
|
||||
return pids;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_MACOS
|
||||
std::vector<Uint64> pids;
|
||||
std::vector<ProcessID> pids;
|
||||
|
||||
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
|
||||
size_t len;
|
||||
@@ -1555,7 +1555,7 @@ std::vector<Uint64> Sys::pidof( const std::string& processName ) {
|
||||
free( procs );
|
||||
return pids;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_BSD
|
||||
std::vector<Uint64> pids;
|
||||
std::vector<ProcessID> pids;
|
||||
|
||||
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 };
|
||||
size_t len;
|
||||
@@ -1586,7 +1586,7 @@ std::vector<Uint64> Sys::pidof( const std::string& processName ) {
|
||||
free( procs );
|
||||
return pids;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_HAIKU
|
||||
std::vector<Uint64> pids;
|
||||
std::vector<ProcessID> pids;
|
||||
int32 cookie = 0;
|
||||
team_info teamInfo;
|
||||
while ( get_next_team_info( &cookie, &teamInfo ) == B_OK ) {
|
||||
@@ -1600,9 +1600,9 @@ std::vector<Uint64> Sys::pidof( const std::string& processName ) {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<std::pair<Uint64, std::string>> Sys::listProcesses() {
|
||||
std::vector<std::pair<ProcessID, std::string>> Sys::listProcesses() {
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
std::vector<std::pair<Uint64, std::string>> pids;
|
||||
std::vector<std::pair<ProcessID, std::string>> pids;
|
||||
std::vector<std::string> extensions = getEnvSplit( "PATHEXT" );
|
||||
|
||||
HMODULE hPsapi = LoadLibrary( TEXT( "psapi.dll" ) );
|
||||
@@ -1655,7 +1655,7 @@ std::vector<std::pair<Uint64, std::string>> Sys::listProcesses() {
|
||||
FreeLibrary( hPsapi );
|
||||
return pids;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_ANDROID
|
||||
std::vector<std::pair<Uint64, std::string>> pids;
|
||||
std::vector<std::pair<ProcessID, std::string>> pids;
|
||||
DIR* dir = opendir( "/proc" );
|
||||
if ( !dir ) {
|
||||
return pids;
|
||||
@@ -1681,7 +1681,7 @@ std::vector<std::pair<Uint64, std::string>> Sys::listProcesses() {
|
||||
closedir( dir );
|
||||
return pids;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_MACOS
|
||||
std::vector<std::pair<Uint64, std::string>> pids;
|
||||
std::vector<std::pair<ProcessID, std::string>> pids;
|
||||
|
||||
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
|
||||
size_t len;
|
||||
@@ -1711,7 +1711,7 @@ std::vector<std::pair<Uint64, std::string>> Sys::listProcesses() {
|
||||
free( procs );
|
||||
return pids;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_BSD
|
||||
std::vector<std::pair<Uint64, std::string>> pids;
|
||||
std::vector<std::pair<ProcessID, std::string>> pids;
|
||||
|
||||
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 };
|
||||
size_t len;
|
||||
@@ -1741,7 +1741,7 @@ std::vector<std::pair<Uint64, std::string>> Sys::listProcesses() {
|
||||
free( procs );
|
||||
return pids;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_HAIKU
|
||||
std::vector<std::pair<Uint64, std::string>> pids;
|
||||
std::vector<std::pair<ProcessID, std::string>> pids;
|
||||
int32 cookie = 0;
|
||||
team_info teamInfo;
|
||||
while ( get_next_team_info( &cookie, &teamInfo ) == B_OK ) {
|
||||
@@ -1832,4 +1832,123 @@ std::string Sys::getUserDirectory() {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Sys::processHasChildren( ProcessID pid ) {
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
|
||||
if ( hSnapshot == INVALID_HANDLE_VALUE ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PROCESSENTRY32 pe32;
|
||||
pe32.dwSize = sizeof( PROCESSENTRY32 );
|
||||
|
||||
if ( !Process32First( hSnapshot, &pe32 ) ) {
|
||||
CloseHandle( hSnapshot );
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
if ( pe32.th32ParentProcessID == static_cast<DWORD>( pid ) ) {
|
||||
CloseHandle( hSnapshot );
|
||||
return true; // Found a child
|
||||
}
|
||||
} while ( Process32Next( hSnapshot, &pe32 ) );
|
||||
|
||||
CloseHandle( hSnapshot );
|
||||
return false;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_ANDROID
|
||||
DIR* dir = opendir( "/proc" );
|
||||
if ( !dir ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dirent* entry;
|
||||
while ( ( entry = readdir( dir ) ) != nullptr ) {
|
||||
char* endptr;
|
||||
long tpid = strtol( entry->d_name, &endptr, 10 );
|
||||
if ( *endptr != '\0' || tpid <= 0 ) { // Skip if not a valid PID directory
|
||||
continue;
|
||||
}
|
||||
std::string status_path = std::string( "/proc/" ) + entry->d_name + "/status";
|
||||
std::ifstream status_file( status_path );
|
||||
if ( status_file.is_open() ) {
|
||||
std::string line;
|
||||
while ( std::getline( status_file, line ) ) {
|
||||
if ( line.rfind( "PPid:", 0 ) == 0 ) {
|
||||
try {
|
||||
long ppid = std::stol( line.substr( 5 ) );
|
||||
if ( ppid == (Int64)pid ) {
|
||||
closedir( dir );
|
||||
return true;
|
||||
}
|
||||
} catch ( const std::invalid_argument& ) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir( dir );
|
||||
return false;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_MACOS || EE_PLATFORM == EE_PLATFORM_IOS || \
|
||||
EE_PLATFORM == EE_PLATFORM_BSD
|
||||
struct kinfo_proc* proc_list = nullptr;
|
||||
size_t proc_count = 0;
|
||||
|
||||
// MIB (Management Information Base) for sysctl
|
||||
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
|
||||
|
||||
// First, call sysctl to get the size of the buffer needed
|
||||
if ( sysctl( mib, 4, nullptr, &proc_count, nullptr, 0 ) < 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
proc_list = (struct kinfo_proc*)malloc( proc_count );
|
||||
if ( !proc_list ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now, call sysctl again to populate the buffer
|
||||
if ( sysctl( mib, 4, proc_list, &proc_count, nullptr, 0 ) < 0 ) {
|
||||
free( proc_list );
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_procs = proc_count / sizeof( struct kinfo_proc );
|
||||
for ( size_t i = 0; i < num_procs; i++ ) {
|
||||
pid_t ppid;
|
||||
#if defined( __APPLE__ )
|
||||
ppid = proc_list[i].kp_eproc.e_ppid;
|
||||
#else // FreeBSD
|
||||
ppid = proc_list[i].ki_ppid;
|
||||
#endif
|
||||
if ( static_cast<ProcessID>( ppid ) == pid ) {
|
||||
free( proc_list );
|
||||
return true; // Found a child
|
||||
}
|
||||
}
|
||||
|
||||
free( proc_list );
|
||||
return false;
|
||||
|
||||
#elif EE_PLATFORM == EE_PLATFORM_HAIKU
|
||||
int32 cookie = 0;
|
||||
team_info ti;
|
||||
while ( get_next_team_info( &cookie, &ti ) == B_OK ) {
|
||||
if ( static_cast<ProcessID>( ti.parent ) == pid ) {
|
||||
return true; // Found a child
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#elif EE_PLATFORM == EE_PLATFORM_EMSCRIPTEN
|
||||
return false;
|
||||
#else
|
||||
#warning "Unsupported operating system: processHasChildren not implemented."
|
||||
return false; // To satisfy compiler, though #error should halt compilation.
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}} // namespace EE::System
|
||||
|
||||
@@ -486,6 +486,10 @@ void UICodeEditorSplitter::loadAsyncFileFromPathInNewTab(
|
||||
|
||||
void UICodeEditorSplitter::setCurrentEditor( UICodeEditor* editor ) {
|
||||
eeASSERT( checkEditorExists( editor ) );
|
||||
if ( !isEditorInAnyWidget( editor ) ) {
|
||||
eeASSERT( true ); // This should not happen by design
|
||||
return;
|
||||
}
|
||||
bool isNew = mCurEditor != editor;
|
||||
bool isNewW = mCurWidget != editor;
|
||||
mCurEditor = editor;
|
||||
@@ -646,6 +650,9 @@ UITabWidget* UICodeEditorSplitter::createTabWidget( Node* parent ) {
|
||||
} );
|
||||
tabWidget->setTabTryCloseCallback(
|
||||
[this]( UITab* tab, UITabWidget::FocusTabBehavior focusTabBehavior ) -> bool {
|
||||
if ( mTabTryCloseCb )
|
||||
return mTabTryCloseCb( tab, focusTabBehavior );
|
||||
|
||||
if ( tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) {
|
||||
tryTabClose( tab->getOwnedWidget()->asType<UICodeEditor>(), focusTabBehavior );
|
||||
return false;
|
||||
@@ -1429,6 +1436,18 @@ bool UICodeEditorSplitter::checkEditorExists( UICodeEditor* checkEditor ) const
|
||||
return found || checkEditor == nullptr || mAboutToAddEditor == checkEditor || mFirstCodeEditor;
|
||||
}
|
||||
|
||||
bool UICodeEditorSplitter::isEditorInAnyWidget( UICodeEditor* checkEditor ) const {
|
||||
bool found = false;
|
||||
forEachEditorStoppable( [&found, checkEditor]( UICodeEditor* editor ) {
|
||||
if ( editor == checkEditor ) {
|
||||
found = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
return found;
|
||||
}
|
||||
|
||||
bool UICodeEditorSplitter::curEditorExists() const {
|
||||
bool found = false;
|
||||
forEachEditorStoppable( [&]( UICodeEditor* editor ) {
|
||||
@@ -1791,4 +1810,9 @@ std::shared_ptr<TextDocument> UICodeEditorSplitter::getTextDocumentRef( TextDocu
|
||||
return ret;
|
||||
}
|
||||
|
||||
void UICodeEditorSplitter::setTabTryCloseCallback( UITabWidget::TabTryCloseCallback cb ) {
|
||||
mTabTryCloseCb = cb;
|
||||
forEachTabWidget( [&cb]( UITabWidget* tw ) { tw->setTabTryCloseCallback( cb ); } );
|
||||
}
|
||||
|
||||
}}} // namespace EE::UI::Tools
|
||||
|
||||
@@ -222,6 +222,7 @@ void AppConfig::load( const std::string& confPath, std::string& keybindingsPath,
|
||||
term.unsupportedOSWarnDisabled =
|
||||
ini.getValueB( "terminal", "unsupported_os_warn_disabled", false );
|
||||
term.closeTerminalTabOnExit = ini.getValueB( "terminal", "close_terminal_tab_on_exit", false );
|
||||
term.warnBeforeClosingTab = ini.getValueB( "terminal", "warn_before_closing_tab", true );
|
||||
|
||||
workspace.restoreLastSession = ini.getValueB( "workspace", "restore_last_session", false );
|
||||
workspace.checkForUpdatesAtStartup =
|
||||
@@ -380,6 +381,7 @@ void AppConfig::save( const std::vector<std::string>& recentFiles,
|
||||
ini.setValue( "terminal", "scrollback", String::toString( term.scrollback ) );
|
||||
ini.setValueB( "terminal", "unsupported_os_warn_disabled", term.unsupportedOSWarnDisabled );
|
||||
ini.setValueB( "terminal", "close_terminal_tab_on_exit", term.closeTerminalTabOnExit );
|
||||
ini.setValueB( "terminal", "warn_before_closing_tab", term.warnBeforeClosingTab );
|
||||
|
||||
ini.setValueB( "window", "vsync", context.VSync );
|
||||
ini.setValue( "window", "glversion",
|
||||
|
||||
@@ -176,6 +176,7 @@ struct TerminalConfig {
|
||||
Uint64 scrollback{ 10000 };
|
||||
bool unsupportedOSWarnDisabled{ false };
|
||||
bool closeTerminalTabOnExit{ false };
|
||||
bool warnBeforeClosingTab{ true };
|
||||
};
|
||||
|
||||
struct WorkspaceConfig {
|
||||
|
||||
@@ -60,6 +60,19 @@ void appLoop() {
|
||||
appInstance->mainLoop();
|
||||
}
|
||||
|
||||
bool App::isAnyTerminalDirty() const {
|
||||
bool dirty = false;
|
||||
mSplitter->forEachWidgetTypeStoppable( UI_TYPE_TERMINAL, [&dirty]( UIWidget* widget ) -> bool {
|
||||
ProcessID pid = widget->asType<UITerminal>()->getTerm()->getTerminal()->getProcess()->pid();
|
||||
if ( Sys::processHasChildren( pid ) ) {
|
||||
dirty = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
return dirty;
|
||||
}
|
||||
|
||||
bool App::onCloseRequestCallback( EE::Window::Window* ) {
|
||||
if ( mSplitter->isAnyEditorDirty() &&
|
||||
( !mConfig.workspace.sessionSnapshot || mCurrentProject.empty() ) ) {
|
||||
@@ -81,6 +94,26 @@ bool App::onCloseRequestCallback( EE::Window::Window* ) {
|
||||
mCloseMsgBox->center();
|
||||
mCloseMsgBox->showWhenReady();
|
||||
return false;
|
||||
} else if ( mConfig.term.warnBeforeClosingTab && isAnyTerminalDirty() ) {
|
||||
if ( mCloseMsgBox )
|
||||
return false;
|
||||
mCloseMsgBox = UIMessageBox::New(
|
||||
UIMessageBox::OK_CANCEL, i18n( "confirm_ecode_exit_terminal_close_warn",
|
||||
"Do you really want to close the code editor?\nAt "
|
||||
"least one terminal is still running a process." )
|
||||
.unescape() );
|
||||
|
||||
mCloseMsgBox->on( Event::OnConfirm, [this]( const Event* ) {
|
||||
saveProject();
|
||||
saveConfig();
|
||||
mWindow->close();
|
||||
} );
|
||||
mCloseMsgBox->on( Event::OnWindowClose, [this]( auto ) { mCloseMsgBox = nullptr; } );
|
||||
mCloseMsgBox->setTitle(
|
||||
String::format( i18n( "close_title", "Close %s?" ).toUtf8(), mWindowTitle ) );
|
||||
mCloseMsgBox->center();
|
||||
mCloseMsgBox->showWhenReady();
|
||||
return false;
|
||||
} else {
|
||||
saveProject();
|
||||
saveConfig();
|
||||
@@ -4119,6 +4152,31 @@ void App::init( InitParameters& params ) {
|
||||
tabWidget->getTabBar()->onDoubleClick(
|
||||
[this]( const MouseEvent* ) { mSplitter->createEditorInNewTab(); } );
|
||||
} );
|
||||
mSplitter->setTabTryCloseCallback(
|
||||
[this]( UITab* tab, UITabWidget::FocusTabBehavior focusTabBehavior ) -> bool {
|
||||
if ( tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) {
|
||||
mSplitter->tryTabClose( tab->getOwnedWidget()->asType<UICodeEditor>(),
|
||||
focusTabBehavior );
|
||||
return false;
|
||||
} else if ( mConfig.term.warnBeforeClosingTab &&
|
||||
tab->getOwnedWidget()->isType( UI_TYPE_TERMINAL ) ) {
|
||||
UITerminal* term = tab->getOwnedWidget()->asType<UITerminal>();
|
||||
ProcessID pid = term->getTerm()->getTerminal()->getProcess()->pid();
|
||||
if ( Sys::processHasChildren( pid ) ) {
|
||||
UIMessageBox* msgBox =
|
||||
UIMessageBox::New( UIMessageBox::OK_CANCEL,
|
||||
i18n( "terminal_close_warn",
|
||||
"Are you sure you want to close this "
|
||||
"terminal?\nIt's still running a process." ) );
|
||||
msgBox->on( Event::OnConfirm, [tab]( auto ) { tab->removeTab(); } );
|
||||
msgBox->setTitle( "ecode" );
|
||||
msgBox->center();
|
||||
msgBox->showWhenReady();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
mPluginManager->setSplitter( mSplitter );
|
||||
|
||||
Log::info( "Base UI took: %.2f ms", globalClock.getElapsedTime().asMilliseconds() );
|
||||
|
||||
@@ -708,6 +708,8 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
|
||||
|
||||
std::string titleFromEditor( UICodeEditor* editor );
|
||||
|
||||
bool isAnyTerminalDirty() const;
|
||||
|
||||
bool onCloseRequestCallback( EE::Window::Window* );
|
||||
|
||||
void addRemainingTabWidgets( Node* widget );
|
||||
|
||||
@@ -168,8 +168,17 @@ void SettingsMenu::createSettingsMenu( App* app, UIMenuBar* menuBar ) {
|
||||
: menuButton );
|
||||
} );
|
||||
|
||||
if ( menuBarIndex == 0 )
|
||||
menu->on( Event::OnMenuHide, [menuHint]( auto ) { menuHint->setVisible( false ); } );
|
||||
if ( menuBarIndex == 0 ) {
|
||||
menu->on( Event::OnMenuHide, [menuHint, this]( auto ) {
|
||||
menuHint->setVisible( false );
|
||||
|
||||
if ( mSplitter->getUISceneNode()->getEventDispatcher()->getFocusNode() ==
|
||||
mSettingsMenu &&
|
||||
mSplitter->getCurWidget() ) {
|
||||
mSplitter->getCurWidget()->setFocus();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
menu->on( Event::OnVisibleChange, [this, menuBarIndex]( const Event* event ) {
|
||||
if ( mApp->getConfig().ui.showMenuBar ) {
|
||||
@@ -963,11 +972,26 @@ UIMenu* SettingsMenu::createTerminalMenu() {
|
||||
mTerminalMenu->addSubMenu( i18n( "new_terminal_behavior", "New Terminal Behavior" ),
|
||||
findIcon( "terminal" ), newTerminalBehaviorSubMenu );
|
||||
|
||||
mTerminalMenu->addSeparator();
|
||||
|
||||
mTerminalMenu
|
||||
->addCheckBox( i18n( "close_terminal_tab_on_exit", "Close Terminal Tab on Exit" ),
|
||||
mApp->getConfig().term.closeTerminalTabOnExit )
|
||||
->setTooltipText( i18n( "close_terminal_tab_on_exit_tooltip",
|
||||
"Closes the terminal tab when the main process exits.\nIf "
|
||||
"disabled, a new shell process starts instead." ) )
|
||||
->setId( "close-terminal-tab-on-exit" );
|
||||
|
||||
mTerminalMenu
|
||||
->addCheckBox( i18n( "warn_before_closing_tab", "Warn Before Closing Tab" ),
|
||||
mApp->getConfig().term.warnBeforeClosingTab )
|
||||
->setTooltipText( i18n( "warn_before_closing_tab_tooltip",
|
||||
"Prompts for confirmation if a program is still running when "
|
||||
"closing a terminal tab." ) )
|
||||
->setId( "warn-before-closing-tab" );
|
||||
|
||||
mTerminalMenu->addSeparator();
|
||||
|
||||
mTerminalMenu
|
||||
->add( i18n( "configure_terminal_shell", "Configure Terminal Shell" ),
|
||||
findIcon( "terminal" ), getKeybind( "configure-terminal-shell" ) )
|
||||
@@ -991,6 +1015,9 @@ UIMenu* SettingsMenu::createTerminalMenu() {
|
||||
mSplitter->forEachWidgetType( UI_TYPE_TERMINAL, [active]( UIWidget* widget ) {
|
||||
widget->asType<UITerminal>()->getTerm()->setKeepAlive( !active );
|
||||
} );
|
||||
} else if ( "warn-before-closing-tab" == id ) {
|
||||
bool active = event->getNode()->asType<UIMenuCheckBox>()->isActive();
|
||||
mApp->getConfig().term.warnBeforeClosingTab = active;
|
||||
} else if ( mSplitter->getCurWidget() &&
|
||||
mSplitter->getCurWidget()->isType( UI_TYPE_TERMINAL ) ) {
|
||||
UITerminal* terminal = mSplitter->getCurWidget()->asType<UITerminal>();
|
||||
@@ -1010,6 +1037,7 @@ UIMenu* SettingsMenu::createTerminalMenu() {
|
||||
|
||||
UIMenu* SettingsMenu::createEditMenu() {
|
||||
mEditMenu = UIPopUpMenu::New();
|
||||
mEditMenu->setId( "edit_menu" );
|
||||
mEditMenu->add( i18n( "undo", "Undo" ), findIcon( "undo" ), getKeybind( "undo" ) )
|
||||
->setId( "undo" );
|
||||
mEditMenu->add( i18n( "redo", "Redo" ), findIcon( "redo" ), getKeybind( "redo" ) )
|
||||
@@ -1034,7 +1062,9 @@ UIMenu* SettingsMenu::createEditMenu() {
|
||||
->add( i18n( "find_replace", "Find/Replace" ), findIcon( "find-replace" ),
|
||||
getKeybind( "find-replace" ) )
|
||||
->setId( "find-replace" );
|
||||
|
||||
mEditMenu->addSeparator();
|
||||
|
||||
mEditMenu
|
||||
->add( i18n( "open_containing_folder_ellipsis", "Open Containing Folder..." ),
|
||||
findIcon( "folder-open" ), getKeybind( "open-containing-folder" ) )
|
||||
@@ -1080,11 +1110,11 @@ UIMenu* SettingsMenu::createEditMenu() {
|
||||
mEditMenu->getItemId( "cut" )->setEnabled( false );
|
||||
mEditMenu->getItemId( "open-containing-folder" )->setVisible( false );
|
||||
mEditMenu->getItemId( "copy-containing-folder-path" )->setVisible( false );
|
||||
moveSep->setEnabled( false )->setVisible( false );
|
||||
mEditMenu->getItemId( "open-in-new-window" )->setVisible( false );
|
||||
mEditMenu->getItemId( "move-to-new-window" )->setVisible( false );
|
||||
fileSep->setEnabled( false )->setVisible( false );
|
||||
mEditMenu->getItemId( "copy-file-path" )->setVisible( false );
|
||||
moveSep->setVisible( false );
|
||||
fileSep->setVisible( false );
|
||||
return;
|
||||
}
|
||||
auto doc = mSplitter->getCurEditor()->getDocumentRef();
|
||||
@@ -1094,11 +1124,11 @@ UIMenu* SettingsMenu::createEditMenu() {
|
||||
mEditMenu->getItemId( "cut" )->setEnabled( doc->hasSelection() );
|
||||
mEditMenu->getItemId( "open-containing-folder" )->setVisible( doc->hasFilepath() );
|
||||
mEditMenu->getItemId( "copy-containing-folder-path" )->setVisible( doc->hasFilepath() );
|
||||
moveSep->setEnabled( true )->setVisible( true );
|
||||
mEditMenu->getItemId( "open-in-new-window" )->setVisible( doc->hasFilepath() );
|
||||
mEditMenu->getItemId( "move-to-new-window" )->setVisible( doc->hasFilepath() );
|
||||
fileSep->setEnabled( doc->hasFilepath() )->setVisible( doc->hasFilepath() );
|
||||
mEditMenu->getItemId( "copy-file-path" )->setVisible( doc->hasFilepath() );
|
||||
moveSep->setVisible( true );
|
||||
fileSep->setVisible( doc->hasFilepath() );
|
||||
} );
|
||||
return mEditMenu;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user