Files
qt5-legatus-legacy/Lts/Common/LtsWidgets/lts_base_layout_widget.h
T
2026-08-13 18:33:10 +02:00

451 lines
18 KiB
C++
Executable File

/**
* @RDE_HEADER@
* Base Layout Widget with all Areas (Info, Main, Detail, Menu)
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "lts_base.h"
#include "lts_widgets_export.h"
#include "lts_notification_box.h"
#include "qevent.h"
#include <memory>
#include <QMap>
#include <QPointer>
#include <QWidget>
class QButtonGroup;
class QPropertyAnimation;
class QPushButton;
class QTimer;
class QToolButton;
namespace Ui
{
class LtsBaseLayoutWidget;
}
namespace IDL
{
enum class PartitionType;
}
namespace LtsUtils { class Promise; }
namespace LtsWidgets
{
class LtsDragIcon;
class LtsProgressDialogHelper;
class LtsTabWidget;
class LtsToolBarArea;
class LtsToolBox;
struct ExerciseData
{
QString exName;
QString exDescription;
int phId = -1;
QString phName;
QString phDescription;
QString exStateString; // Already translated. Todo: Probably won't change, if Language is changed on the fly.
ExcState exState = ExerciseState::eUnknown;
QColor exStateCol;
QString partitionType;
bool operator==(const ExerciseData &o) const
{
return exName == o.exName &&
exDescription == o.exDescription &&
phId == o.phId &&
phName == o.phName &&
phDescription == o.phDescription &&
exStateString == o.exStateString &&
exState == o.exState &&
exStateCol == o.exStateCol &&
partitionType == o.partitionType;
}
bool operator!=(const ExerciseData &o) const
{
return !operator ==(o);
}
};
/**
* @brief The LtsBaseLayoutWidget class represents the Patric-Layout-System (all Areas: Info, Main, Detail, Menu), which is used as stand-alone widget per Screen.
* It is created per Session by the LtsSession, created by the LtsSessionManager whenever a new Session was created.
*/
class LTSWIDGETS_EXPORT LtsBaseLayoutWidget : public QWidget
{
Q_OBJECT
public:
explicit LtsBaseLayoutWidget(QWidget *parent = nullptr, bool splitMenu = true);
~LtsBaseLayoutWidget();
/**
* @brief hideVisibleLayouts hides all layouts without interfering layout sizes and saves all layout's old states that can be restored by calling resetLayoutVisibility
*/
void hideVisibleLayouts();
/**
* @brief resetLayoutVisibility reverts the actions done by hideVisibleLayouts - reshows all hidden layouts
*/
void resetLayoutVisibility();
/**
* @brief setMenuVisible sets the visibility of the menu (and the special tab, too!)
* @param visible true to reshow the menu and special tab, false to completely hide them
*/
void setMenuVisible(bool visible);
/**
* @brief isMenuVisible returns the visibility state of the menu (and special tab, too!)
* @return true if both are visibly, false if not
*/
bool isMenuVisible() const;
bool isTopBarVisible() const;
void setTopBarVisible(bool visible);
/**
* @brief isMenuCollapsed returns if the menu was collapsed programmatically by calling clappseMenu.
* NOTE: does not consider the collapsed state if the user collapsed the menu himself by reducing the menu size to zero by moving the splitter.
* @return true is collapsed before, false if menu is not size zero.
*/
bool isMenuCollapsed() const;
/**
* @brief collapseMenu reduces the size of the menu to zero, but the visibility doesn't change, so that the user can still restore the menu with the splitter.
* NOTE: the special tab widget is collapsed, too.
* @param collapse true to collapse, false to restore to the state before the collapse.
*/
void collapseMenu(bool collapse = true);
/**
* @brief setSessionInfo sets the informations about the Session of this BaseLayout, so that the window can request a Session close (by logging out) when this window receives a close request.
*/
void setSessionInfo(const int domainId, const IDL::PartitionType &partitionType, const int partitionId, const QString &workspaceId);
/**
* @brief addMenuAction adds an action (aka. menu entry) to the menu layout. The Action will be added into the given group.
* @param plData Action data with information about where to put the menu entry and what to do when the menu entry is clicked
* @return the LtsDragIcon that was now created and put into the menu layout
*/
LtsDragIcon *addMenuAction(const PluginData &plData, bool isFirstRun = false);
/**
* @brief removeMenuAction removes an action (aka. menu entry) previously created by the addMenuAction method by identifying it by the information in the given plData
* @param plData Action data to identify the menu entry with (group name + captioin text of the menu entry)
* @return true when found & deleted, false when not found and also not deleted
*/
bool removeMenuAction(const PluginData &plData);
/**
* @brief removeMenuAction removes an action (aka. menu entry) previously created by the addMenuAction method by identifying it by the pointer value
* @param icon to identify the action (aka. menu entry) by the menu entry the LtsDragIcon that was created by the addMenuAction method and put into the menu layout
* @return true when found & deleted, false when not found and also not deleted (as it was deleted before)
*/
bool removeMenuAction(LtsDragIcon *icon);
bool removeMenuAction(const QString &byIdentifier);
/**
* @brief clearMenuActions deletes all actions (aka. menu entries) in the menu layout
*/
void clearMenuActions();
void setAppName(const QString &appName);
bool isDialogFocused(const LtsDialog *dialog) const;
/**
* @brief setCustomText shows a custom text to the top layout of the base layout, right after the partition type text.
* Also prepends a bullet before the text to clearly differenciate between the partition type text and the custom text.
* Call with an empty string to hide the custom text again.
* @param text to print on the label
*/
void setCustomText(const QString &text);
const QString customText() const;
/**
* @brief getTabFromLayoutArea returns the pointer to the LtsTabWidget that is of the type LayoutArea 'area'
* @param area the area of which the LtsTabWidget's type of
* @return the pointer to the LtsTabWidget
*/
LtsTabWidget *getTabFromLayoutArea(LayoutArea area) const;
/** TODO: rename to getDialog
* @brief getWidget returns the Dialog which is located in the given LayoutArea, identified by the group + identifier strings
* @param menuGroup the name of the main tab of the LayoutArea
* @param identifier the name of the sub tab (when sub-tabbed) of the LayoutArea, otherwise the identifier is added to the menuGroup string
* @param area the LayoutArea (/LtsTabWidget) the Dialog is located
* @return the LtsDialog when found, otherwise nullptr when not found
*/
LtsDialog *getWidget(const QString &menuGroup, const QString &identifier, LayoutArea area = LayoutArea::eUnknown);
/**
* @brief getMenu returns the Widget of the Menu Layout Area (as the Menu is not an actual LayoutArea (not an LtsTabWidget which could be returned by 'getTabFromLayoutArea')
* @return the pointer to the Widget of the Menu Layout
*/
QWidget *getMenu() const;
/**
* @brief getToolbar returns the LtsToolBarArea of the Menu Layout Area
* @return the pointer to the LtsToolBarArea of the Menu Layout
*/
LtsToolBarArea *getToolbar() const;
/**
* @brief setOperatorName sets the operator name of this session's operator and sets the operator name widget's text
* @param operatorName the human readable operator name of this session's operator
*/
void setOperatorName(const QString &operatorName, bool showTerminalButton);
/**
* @brief setProfilePicture sets the profile picture of this session's operator and sets the profile picture widget's picture
* @param profilePicture the profgile picture of this session's operator
*/
void setProfilePicture(const QPixmap &profilePicture);
/**
* @brief setExerciseData sets the name and phase of this Exercise (if it is an Exercise) of this session - only sets the text of a label (label is hidden of not set)
* @param exerciseData necessary data to show
*/
void setExerciseData(const ExerciseData &data);
/**
* @brief Call this function when the base layout was properly constructed so baselayout can do some internal stuff
*/
void initilizationFinished();
/**
* @brief addNotification [Do not use.] used by LtsSession to add a Notification to the Notification Bar, but as a QToolButton
* @param noti the noti to add to the Notification Bar
*/
void addNotification(QToolButton *noti);
/**
* @brief isWidgetsHidden returns the visibility state of the detail tab - the state changes with calling either of
* these methods: onReShowAnimated, onHideAnimated, showWidgets, hideWidgets
* @return detail tab visible (not to be confused with resizing the detail widget with the splitter, that only
* minized the tab, but the tab is still visible)
*/
bool isWidgetsHidden() const;
bool notificationExists(const QString &identifier) const;
const QMap<LtsDragIcon *, PluginData> &mapButtons() const { return m_mapButtons; }
void setToolBarActions(const QStringList &actions);
QStringList toolBarActions() const;
void setFunctionKeys(const QMap<int, QString> &actions);
QMap<int, QString> functionKeys() const;
void setWorkspaceId(const QString &workspaceId) { m_workspaceId = workspaceId; }
/**
* @brief showMessage adds a notification bubble to the bottom right of the blw. Text can be formatted with HTML.
* If a message is already visible, it will be replaced and will be shown after the new message was closed.
* A message with the same identifier will be replaced in the queue or if currently shown.
* @param title The title in bold which is show obove the text.
* @param text The text to show in the bubble - should be as short as possible as bubbles are small. Can be HTML.
* @param type The type which sets the color and icon the bubble should have. See LtsNotificationBox::MessageType.
* @param clickedLinkCallback The function to call if a link inside the bubble has been clicked (HTML 'a' tag)
* @param closeOnLinkClicked Closes the bubble if a link has been clicked. Note: queued messages (if any) will be shown next
* @param identifier An invisible identifier a message will be queued with. Can be left empty. If another message with the
* same identifier (if not empty) is shown, the other message with the same identifier will be removed from the queue or if
* it's the shown message, it will be closed.
*/
void showMessage(const QString &title, const QString &text, LtsNotificationBox::MessageType type = LtsNotificationBox::MessageType::Info, const std::function<void(const QString &link)> &clickedLinkCallback = nullptr, bool closeOnLinkClicked = true, const QString &identifier = {});
bool messageIdentifierExists(const QString &identifier) const;
void executeAction(const QString &actionIdentifier);
bool isSecondaryWidget() const { return m_isSecondaryWidget; } // for the LtsScreenContainer to determine if this is a 'Hilfsansicht' for GUEZ SWP 2023 (https://devhub.defence-elec.de/confluence/x/yLjeDg) (siehe 1.1.1)
void setSecondaryWidget() { m_isSecondaryWidget = true; }
/**
* @brief Remove Messages from Queue, as it's handled by someones else
* @param message identifier to remote from que or diplay.
*/
void removeQueuedMessage(const QString &messageIdentifier);
public slots:
bool slotClose(); // checks for unsaved changes first and aborts if there are any
void slotLogout(); // checks for unsaved changes first and aborts if there are any
void slotScreenCountChanged(int count);
/**
* @brief onMenuEntryClicked is triggered when an action (aka. menu entry) in the menu layout is clicked, executes the action's payload (or focuses the coherant dialog when already open)
*/
void onMenuEntryClicked();
void onHideAnimated();
void onReShowAnimated();
/**
* @brief addWidget adds a widget to the operator-specified base layout (either selection by cursor or by permanent selectioin) NOTE: deletes dialog if it already exists
* @param widget the Dialog to be added
* @param menuGroup the main tab identified by 'menuGroup' (tab is created when tab not existent) to add the Dialog into
* @param identifier the sub tab (when LtsTabWidget is sub-tabbed, otherwise only added to tab identified by menuGroup + identifier), identified by 'identifier' to add the Dialog into - keep empty to prevent checking for existing tabs
* @param area the LayoutArea to add the Dialog into
* @param setClosable true when closable by operator (shows a close-button in the tab), false when not closable by operator
* @return true when added, false when not (invalid widget or non-existant LayoutArea)
*/
bool addWidget(LtsWidgets::LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area = LayoutArea::eUnknown, bool setClosable = true, bool focusNewTab = true);
/**
* @brief Like addWidget addOrReplaceWidget adds a dialog widget to base layout with the difference that an existing widget with same identifier is removed from layout before adding he new one.
* The new dialog will not be deleted (compare with adddWidget)
* @param widget the Dialog to be added
* @param menuGroup the main tab identified by 'menuGroup' (tab is created when tab not existent) to add the Dialog into
* @param identifier the sub tab (when LtsTabWidget is sub-tabbed, otherwise only added to tab identified by menuGroup + identifier), identified by 'identifier' to add the Dialog into - keep empty to prevent checking for existing tabs
* @param area the LayoutArea to add the Dialog into
* @param setClosable true when closable by operator (shows a close-button in the tab), false when not closable by operator
* @return true when added, false when not (invalid widget or non-existant LayoutArea)
* @see addWidget
*/
bool addOrReplaceWidget(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LayoutArea area = LayoutArea::eUnknown, bool setClosable = true, bool focusNewTab = true );
bool focusTab(const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area);
/**
* @brief executeCreateInstantFunctions executes both the createWidgetFunc and the directFunc (if available). (only used by LtsSession & this addMenuAction)
* @param plData
*/
void executeCreateInstantFunctions(const LtsWidgets::PluginData &plData);
void setExerciseTime(qint64 mSecsSinceEpoch);
bool haveUnsavedChanges();
/**
* @brief notifyUnsavedChanges
* @return true if there were NO unsaved changes (method 'failed') and vice versa
*
*/
bool notifyUnsavedChanges();
// LtsUtils::Promise *notifyUnsavedChangesAsync();
void setIrreplaceableOnce() { m_irreplaceableOnce = true; }
bool irreplaceableReset() { bool r = m_irreplaceableOnce; m_irreplaceableOnce = false; return r; }
bool isIrreplaceable() const { return m_irreplaceableOnce; }
/**
* @brief showWidgets shows the Dialog Layout again after it was previously hidden
*/
void showWidgets();
/**
* @brief hideWidgets hides the visible Dialog Layout (for example e.g. when more space is needed for selection on the map)
*/
void hideWidgets();
private slots:
/**
* @brief slotOperatorMenuClicked shows a menu to change things affecting the operator (logout, etc.)
*/
void slotOperatorMenuClicked();
void setCurrentTime();
signals:
/**
* @brief signalCloseRequest is emitted by the closeEvent and requests a logout / close of the current Session (if this is the last BaseLayout)
*/
//void signalCloseRequest(const int domainId, const IDL::PartitionType &partitionType, const int partitionId);
void signalClose();
void signalLogout();
void signalShowOperatorSettings();
void signalRetranslateActions();
void signalSwap();
void signalStapleTopicLockHandler(LtsWidgets::LtsDialog *widget);
void signalSuccessfullyPlaced(); // called by MMM if this widget was successfully placed (even works if MMM is inactive (off))
/**
* Notify about unsaved changes before popup message box.
*/
void signalNotifyUnsavedChanges(bool &reject);
protected:
/**
* @brief closeEvent requests to close a session. As a BaseLayout is linked to a Session, the Session must be closed before allowing
* the BaseLayout to close. So a logout is executed before closing the BaseLayout.
* TODO: One or more BaseLayouts can be linked to a Session - look if it's the last BaseLayout
* @param event
*/
void closeEvent(QCloseEvent *event) override;
void showEvent(QShowEvent *event) override;
void changeEvent(QEvent *e) override;
private:
void addDebugMenu();
void retranslate();
void addTouchScroll(QWidget *widget);
LtsDragIcon *createButton(const PluginData &plData);
int getIndexOfTab(LtsTabWidget *tab);
LtsToolBox *getToolBox(const ExercisePart &exercisePart) const;
QPushButton *getExercisePartButton(const ExercisePart &exercisePart) const;
void checkExercisePartButtonVisibility();
void checkMenuGroup(const QString &menuGroup, const ExercisePart &exercisePart);
bool isDialogFocused(const LtsDialog *dialog, const LayoutArea &layoutArea) const;
bool isDialogFocused(const LtsDialog *dialog, const LtsTabWidget *tab) const;
void applyExerciseStateRestricition();
void executePluginData(const PluginData &plData);
void showNextQueuedMessage();
//void removeQueuedMessage(const QString &messageIdentifier);
struct Message
{
public:
QString title;
QString text;
LtsNotificationBox::MessageType type;
std::function<void(const QString &link)> clickedLinkCallback;
bool closeOnLinkClicked;
QString identifier;
};
ExerciseData m_exerciseData;
QMap<LtsDragIcon *, PluginData> m_mapButtons;
QList<int> m_mainSplitterSizes;
QList<int> m_menuSplitterSizes;
bool m_isHidden;
QPropertyAnimation *m_currentAnimation{nullptr};
Ui::LtsBaseLayoutWidget *m_ui{nullptr};
IDL::PartitionType m_partitionType;
int m_domainId;
int m_partitionId;
QString m_operatorName;
bool m_firstShow;
bool m_showTerminalButton;
qint64 m_exerciseTime;
QTimer *m_setCurrentTimeTimer{nullptr};
bool m_irreplaceableOnce;
QButtonGroup *m_bg{nullptr};
QString m_workspaceId;
int m_screenCount;
bool m_menuCollapsed;
QMap<QPointer<LtsWidgets::LtsTabWidget>, bool> m_oldLayoutVisibilityStates;
LtsNotificationBox *m_notiBox{nullptr};
QStringList m_notFoundYet;
bool m_isSecondaryWidget { false };
QVector<Message> m_currentMessage; // optional
QVector<Message> m_messageQueue;
};
} // namespace LtsWidgets