diff --git a/OpenBoard.pro b/OpenBoard.pro index 73e52d511..93c8b8c78 100644 --- a/OpenBoard.pro +++ b/OpenBoard.pro @@ -84,6 +84,7 @@ UB_ETC.files = resources/etc UB_I18N.files = resources/i18n/*.qm UB_LIBRARY.files = resources/library UB_FONTS.files = resources/fonts +UB_STARTUP_HINTS.files = resources/startupHints UB_THIRDPARTY_INTERACTIVE.files = thirdparty/interactive DEFINES += NO_THIRD_PARTY_WARNINGS @@ -138,6 +139,7 @@ win32 { UB_LIBRARY.path = $$DESTDIR UB_I18N.path = $$DESTDIR/i18n UB_ETC.path = $$DESTDIR + UB_STARTUP_HINTS.path = $$DESTDIR UB_THIRDPARTY_INTERACTIVE.path = $$DESTDIR/library system(md $$replace(BUILD_DIR, /, \\)) system(echo "$$VERSION" > $$BUILD_DIR/version) @@ -217,6 +219,8 @@ macx { UB_LIBRARY.path = "$$RESOURCES_DIR" UB_FONTS.files = "resources/fonts" UB_FONTS.path = "$$RESOURCES_DIR" + UB_STARTUP_HINTS.files = "resources/startupHints" + UB_STARTUP_HINTS.path = "$$RESOURCES_DIR" UB_THIRDPARTY_INTERACTIVE.files = $$files($$THIRD_PARTY_PATH/interactive/*) UB_THIRDPARTY_INTERACTIVE.path = "$$RESOURCES_DIR/library/interactive" UB_MACX_ICNS.files = $$files(resources/macx/*.icns) @@ -439,6 +443,7 @@ macx { QMAKE_BUNDLE_DATA += UB_ETC \ UB_LIBRARY \ UB_FONTS \ + UB_STARTUP_HINTS \ UB_THIRDPARTY_INTERACTIVE \ UB_MACX_ICNS \ UB_MACX_EXTRAS \ @@ -478,6 +483,7 @@ linux-g++* { UB_LIBRARY.path = $$DESTDIR UB_I18N.path = $$DESTDIR/i18n UB_ETC.path = $$DESTDIR + UB_STARTUP_HINTS.path = $$DESTDIR UB_THIRDPARTY_INTERACTIVE.path = $$DESTDIR/library system(mkdir -p $$BUILD_DIR) system(echo "$$VERSION" > $$BUILD_DIR/version) @@ -527,6 +533,7 @@ TRANSLATIONS = resources/i18n/OpenBoard_ar.ts \ INSTALLS = UB_ETC \ UB_I18N \ UB_LIBRARY \ + UB_STARTUP_HINTS \ UB_THIRDPARTY_INTERACTIVE DISTFILES += \ diff --git a/README.md b/README.md index ffc226523..96fc6f016 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ OpenBoard is an open-source cross-platform interactive white board application designed primarily for use in schools. It was originally forked from Open-Sankoré, which was itself based on Uniboard. +![OpenBoard startup hints screen](image/README/openboard-startup-hints.png) + ### Installing 1.7.7 installers are available for Windows, macOS and Debian on the [Downloads page](https://github.com/OpenBoard-org/OpenBoard/wiki/Downloads). diff --git a/RUN_LOCAL_WINDOWS.md b/RUN_LOCAL_WINDOWS.md new file mode 100644 index 000000000..3b034ad89 --- /dev/null +++ b/RUN_LOCAL_WINDOWS.md @@ -0,0 +1,86 @@ +# Run This Local Windows Build + +This file documents the local Windows setup used to build and run this modified OpenBoard checkout. + +## Quick Run + +The app has already been built and deployed here: + +```powershell +D:\Ai\product\own\OpenBoard\OpenBoard\build\win32\release\product\OpenBoard.exe +``` + +Double-click `OpenBoard.exe`, or run: + +```powershell +cd D:\Ai\product\own\OpenBoard\OpenBoard\build\win32\release\product +.\OpenBoard.exe +``` + +## What Was Installed + +This local build uses: + +```text +Qt 6.8.3 MSVC 2022 x64: D:\Ai\Qt\6.8.3\msvc2022_64 +OpenBoard source: D:\Ai\product\own\OpenBoard\OpenBoard +Third-party libraries: D:\Ai\product\own\OpenBoard\OpenBoard-ThirdParty +Windows SDK: 10.0.18362 +Visual Studio Build Tools +``` + +## Rebuild After Code Changes + +Open a normal PowerShell terminal and run: + +```powershell +cd D:\Ai\product\own\OpenBoard\OpenBoard + +cmd.exe /d /s /c '"C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\Common7\Tools\VsDevCmd.bat" -arch=amd64 && "D:\Ai\Qt\6.8.3\msvc2022_64\bin\qmake.exe" OpenBoard.pro && nmake' +``` + +If `quazip.lib` is missing, rebuild QuaZip first: + +```powershell +cmd.exe /d /s /c '"C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\Common7\Tools\VsDevCmd.bat" -arch=amd64 && cd /d "D:\Ai\product\own\OpenBoard\OpenBoard-ThirdParty\quazip" && "D:\Ai\Qt\6.8.3\msvc2022_64\bin\qmake.exe" quazip.pro && nmake release' +``` + +Then rebuild OpenBoard again. + +## Deploy Runtime Files + +After a successful build, copy OpenBoard resources and deploy Qt dependencies: + +```powershell +cd D:\Ai\product\own\OpenBoard\OpenBoard + +cmd.exe /d /s /c '"C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\Common7\Tools\VsDevCmd.bat" -arch=amd64 && cd /d "D:\Ai\product\own\OpenBoard\OpenBoard" && nmake install' + +& D:\Ai\Qt\6.8.3\msvc2022_64\bin\windeployqt.exe --release --compiler-runtime --qmldir src build\win32\release\product\OpenBoard.exe + +$product = Resolve-Path build\win32\release\product +Copy-Item ..\OpenBoard-ThirdParty\poppler\bin\*.dll -Destination $product -Force +Copy-Item ..\OpenBoard-ThirdParty\openssl\openssl-3.0.15-win64\bin\*.dll -Destination $product -Force +Copy-Item ..\OpenBoard-ThirdParty\zlib\1.2.11\bin\*.dll -Destination $product -Force +``` + +## Test The New Changes + +After launching OpenBoard: + +- Confirm the toolbar shows the new `Minimize` button. +- Click `Minimize` and confirm OpenBoard goes to the Windows taskbar. +- Restore OpenBoard from the taskbar. +- Check the system tray icon menu for `Show OpenBoard`, `Minimize`, and `Exit`. +- Open `Preferences -> Display`. +- Enable `Run in windowed mode (shows minimize/maximize/close buttons)`. +- Restart OpenBoard and confirm the native Windows title bar appears. + +Fullscreen mode remains the default when the windowed mode preference is disabled. + +## Notes + +- This checkout is nested one level below `D:\Ai\product\own\OpenBoard`. +- `OpenBoard-ThirdParty` must sit next to the `OpenBoard` source folder. +- If `rc.exe` is missing, install the Windows SDK. +- If `qmake` resolves to Anaconda Qt 5, use the full Qt 6 qmake path shown above. diff --git a/image/README/openboard-startup-hints.png b/image/README/openboard-startup-hints.png new file mode 100644 index 000000000..579d919e1 Binary files /dev/null and b/image/README/openboard-startup-hints.png differ diff --git a/resources/forms/mainWindow.ui b/resources/forms/mainWindow.ui index e7c76e60d..73ac224fc 100644 --- a/resources/forms/mainWindow.ui +++ b/resources/forms/mainWindow.ui @@ -82,6 +82,7 @@ + @@ -132,6 +133,7 @@ + @@ -186,6 +188,7 @@ + @@ -1849,10 +1852,10 @@ :/images/toolbar/hide.png:/images/toolbar/hide.png - Hide OpenBoard + Minimize - Hide OpenBoard Application + Minimize OpenBoard Ctrl+H diff --git a/resources/forms/preferences.ui b/resources/forms/preferences.ui index 45a2fc180..1d10f8cf9 100644 --- a/resources/forms/preferences.ui +++ b/resources/forms/preferences.ui @@ -106,7 +106,7 @@ 783 - 765 + 790 @@ -252,7 +252,7 @@ 0 - 620 + 645 1011 101 @@ -524,7 +524,7 @@ 0 540 1011 - 71 + 96 @@ -578,6 +578,13 @@ + + + + Run in windowed mode (shows minimize/maximize/close buttons) + + + diff --git a/src/core/UBApplication.cpp b/src/core/UBApplication.cpp index 640fad511..d0973bf9b 100644 --- a/src/core/UBApplication.cpp +++ b/src/core/UBApplication.cpp @@ -99,6 +99,7 @@ UBApplication::UBApplication(const QString &id, int &argc, char **argv) : Single , mPreferencesController(NULL) , mApplicationTranslator(NULL) , mQtGuiTranslator(NULL) + , mSystemTrayIcon(NULL) { Q_UNUSED(id) staticMemoryCleaner = new QObject(0); // deleted in UBApplication destructor @@ -165,6 +166,12 @@ UBApplication::~UBApplication() UBFileSystemUtils::deleteAllTempDirCreatedDuringSession(); + if (mSystemTrayIcon) + { + mSystemTrayIcon->hide(); + mSystemTrayIcon->setContextMenu(nullptr); + } + delete mainWindow; mainWindow = 0; @@ -374,16 +381,13 @@ int UBApplication::exec(const QString& pFileToImport) connect(mainWindow->actionDesktop, SIGNAL(triggered(bool)), applicationController, SLOT(showDesktop(bool))); connect(mainWindow->actionDesktop, SIGNAL(triggered(bool)), this, SLOT(stopScript())); -#if defined(Q_OS_OSX) || defined(Q_OS_LINUX) connect(mainWindow->actionHideApplication, SIGNAL(triggered()), this, SLOT(showMinimized())); -#else - connect(mainWindow->actionHideApplication, SIGNAL(triggered()), mainWindow, SLOT(showMinimized())); -#endif mPreferencesController = new UBPreferencesController(mainWindow); connect(mainWindow->actionPreferences, SIGNAL(triggered()), mPreferencesController, SLOT(show())); connect(mainWindow->actionCheckUpdate, SIGNAL(triggered()), applicationController, SLOT(checkUpdateRequest())); + setupSystemTrayIcon(); toolBarPositionChanged(UBSettings::settings()->appToolBarPositionedAtTop->get()); @@ -441,10 +445,56 @@ void UBApplication::showMinimized() #elif defined(Q_OS_LINUX) mainWindow->showMinimized(); bIsMinimized = true; +#else + mainWindow->showMinimized(); #endif } +void UBApplication::restoreMainWindow() +{ + if (!mainWindow) + return; + +#if defined(Q_OS_OSX) || defined(Q_OS_LINUX) + bIsMinimized = false; +#endif + + if (UBSettings::settings()->appRunInWindow->get().toBool()) + mainWindow->showNormal(); + else + UBPlatformUtils::showFullScreen(mainWindow); + + mainWindow->raise(); + mainWindow->activateWindow(); +} + +void UBApplication::setupSystemTrayIcon() +{ + if (!QSystemTrayIcon::isSystemTrayAvailable()) + return; + + mSystemTrayIcon = new QSystemTrayIcon(QIcon(":/images/OpenBoard.png"), this); + mSystemTrayIcon->setToolTip(tr("OpenBoard")); + + QMenu* trayMenu = new QMenu(mainWindow); + QAction* showAction = trayMenu->addAction(tr("Show OpenBoard")); + QAction* minimizeAction = trayMenu->addAction(tr("Minimize")); + trayMenu->addSeparator(); + QAction* exitAction = trayMenu->addAction(tr("Exit")); + + connect(showAction, SIGNAL(triggered()), this, SLOT(restoreMainWindow())); + connect(minimizeAction, SIGNAL(triggered()), this, SLOT(showMinimized())); + connect(exitAction, SIGNAL(triggered()), this, SLOT(closing())); + connect(mSystemTrayIcon, &QSystemTrayIcon::activated, this, [this](QSystemTrayIcon::ActivationReason reason) { + if (reason == QSystemTrayIcon::Trigger || reason == QSystemTrayIcon::DoubleClick) + restoreMainWindow(); + }); + + mSystemTrayIcon->setContextMenu(trayMenu); + mSystemTrayIcon->show(); +} + void UBApplication::startScript() { diff --git a/src/core/UBApplication.h b/src/core/UBApplication.h index ab13cf832..5d74d3bef 100644 --- a/src/core/UBApplication.h +++ b/src/core/UBApplication.h @@ -34,6 +34,7 @@ #include #include #include +#include #include "singleapplication/singleapplication.h" @@ -135,10 +136,12 @@ class UBApplication : public SingleApplication //#ifdef Q_OS_OSX // for some reason this is not compiled if the ifdef is uncommented void showMinimized(); //#endif + void restoreMainWindow(); void onScreenCountChanged(int newCount); private: void updateProtoActionsState(); + void setupSystemTrayIcon(); void setupTranslators(QStringList args); QList mProtoMenus; bool mIsVerbose; @@ -153,6 +156,7 @@ class UBApplication : public SingleApplication UBPreferencesController* mPreferencesController; QTranslator* mApplicationTranslator; QTranslator* mQtGuiTranslator; + QSystemTrayIcon* mSystemTrayIcon; }; diff --git a/src/core/UBPreferencesController.cpp b/src/core/UBPreferencesController.cpp index ed0e393d8..05de68fce 100644 --- a/src/core/UBPreferencesController.cpp +++ b/src/core/UBPreferencesController.cpp @@ -190,6 +190,7 @@ void UBPreferencesController::wire() settings->boardKeyboardPaletteKeyBtnSize->setString(mPreferencesUI->keyboardPaletteKeyButtonSize->itemText(index)); }); connect(mPreferencesUI->startModeComboBox, SIGNAL(currentIndexChanged(int)), settings->appStartMode, SLOT(setInt(int))); + connect(mPreferencesUI->runInWindowCheckBox, SIGNAL(clicked(bool)), settings->appRunInWindow, SLOT(setBool(bool))); connect(mPreferencesUI->useExternalBrowserCheckBox, SIGNAL(clicked(bool)), settings->webUseExternalBrowser, SLOT(setBool(bool))); connect(mPreferencesUI->displayBrowserPageCheckBox, SIGNAL(clicked(bool)), settings->webShowPageImmediatelyOnMirroredScreen, SLOT(setBool(bool))); @@ -320,6 +321,7 @@ void UBPreferencesController::init() mPreferencesUI->emptyTrashDaysValue->setValue(settings->emptyTrashDaysValue->get().toInt()); mPreferencesUI->startModeComboBox->setCurrentIndex(settings->appStartMode->get().toInt()); + mPreferencesUI->runInWindowCheckBox->setChecked(settings->appRunInWindow->get().toBool()); mPreferencesUI->useExternalBrowserCheckBox->setChecked(settings->webUseExternalBrowser->get().toBool()); mPreferencesUI->displayBrowserPageCheckBox->setChecked(settings->webShowPageImmediatelyOnMirroredScreen->get().toBool()); @@ -383,6 +385,7 @@ void UBPreferencesController::defaultSettings() mPreferencesUI->verticalChoice->setChecked(settings->appToolBarOrientationVertical->reset().toBool()); mPreferencesUI->horizontalChoice->setChecked(!settings->appToolBarOrientationVertical->reset().toBool()); mPreferencesUI->startModeComboBox->setCurrentIndex(0); + mPreferencesUI->runInWindowCheckBox->setChecked(settings->appRunInWindow->reset().toBool()); mPreferencesUI->useSystemOSKCheckBox->setChecked(settings->useSystemOnScreenKeyboard->reset().toBool());