From 78e1cee86c75941f9936110334b9e10d1f4db869 Mon Sep 17 00:00:00 2001 From: ahosier Date: Thu, 5 Sep 2024 10:32:44 -0400 Subject: [PATCH] QtCommon v4.1.0 --- CMakeLists.txt | 2 +- .../qt_common/custom_widgets/CMakeLists.txt | 15 + .../custom_widgets/banner_widget.cpp | 137 +++ .../qt_common/custom_widgets/banner_widget.h | 100 +++ .../qt_common/custom_widgets/banner_widget.ui | 174 ++++ .../custom_widgets/check_box_widget.cpp | 7 + .../custom_widgets/check_box_widget.h | 13 +- .../custom_widgets/driver_overrides_model.cpp | 823 ++++++++++++++++++ .../custom_widgets/driver_overrides_model.h | 440 ++++++++++ .../driver_overrides_notification_banner.cpp | 78 ++ .../driver_overrides_notification_banner.h | 65 ++ ...r_overrides_notification_config_widget.cpp | 92 ++ ...ver_overrides_notification_config_widget.h | 65 ++ ...er_overrides_notification_config_widget.ui | 153 ++++ .../driver_overrides_tree_widget.cpp | 137 +++ .../driver_overrides_tree_widget.h | 51 ++ .../driver_overrides_tree_widget.ui | 140 +++ .../custom_widgets/navigation_list_widget.cpp | 2 + .../custom_widgets/scaled_link_label.cpp | 103 +++ .../custom_widgets/scaled_link_label.h | 85 ++ .../custom_widgets/shared_isa_tree_view.cpp | 84 +- .../custom_widgets/shared_isa_widget.cpp | 2 +- source/qt_common/utils/common_definitions.h | 11 + source/qt_common/utils/qt_util.cpp | 59 +- source/qt_common/utils/qt_util.h | 7 - 25 files changed, 2764 insertions(+), 81 deletions(-) create mode 100644 source/qt_common/custom_widgets/banner_widget.cpp create mode 100644 source/qt_common/custom_widgets/banner_widget.h create mode 100644 source/qt_common/custom_widgets/banner_widget.ui create mode 100644 source/qt_common/custom_widgets/driver_overrides_model.cpp create mode 100644 source/qt_common/custom_widgets/driver_overrides_model.h create mode 100644 source/qt_common/custom_widgets/driver_overrides_notification_banner.cpp create mode 100644 source/qt_common/custom_widgets/driver_overrides_notification_banner.h create mode 100644 source/qt_common/custom_widgets/driver_overrides_notification_config_widget.cpp create mode 100644 source/qt_common/custom_widgets/driver_overrides_notification_config_widget.h create mode 100644 source/qt_common/custom_widgets/driver_overrides_notification_config_widget.ui create mode 100644 source/qt_common/custom_widgets/driver_overrides_tree_widget.cpp create mode 100644 source/qt_common/custom_widgets/driver_overrides_tree_widget.h create mode 100644 source/qt_common/custom_widgets/driver_overrides_tree_widget.ui create mode 100644 source/qt_common/custom_widgets/scaled_link_label.cpp create mode 100644 source/qt_common/custom_widgets/scaled_link_label.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b37f9a6..a4d58a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(QtCommon) # Define version information set(QTCOMMON_MAJOR_VERSION 4) -set(QTCOMMON_MINOR_VERSION 0) +set(QTCOMMON_MINOR_VERSION 1) if (NOT QTCOMMON_PATCH_NUMBER) set(QTCOMMON_PATCH_NUMBER 0) endif () diff --git a/source/qt_common/custom_widgets/CMakeLists.txt b/source/qt_common/custom_widgets/CMakeLists.txt index c870edc..2103ada 100644 --- a/source/qt_common/custom_widgets/CMakeLists.txt +++ b/source/qt_common/custom_widgets/CMakeLists.txt @@ -7,12 +7,17 @@ set(CMAKE_AUTOUIC ON) # Add all header and source files within the directory to the library. file (GLOB CPP_INC "arrow_icon_combo_box.h" + "banner_widget.h" "check_box_widget.h" "colored_legend_graphics_view.h" "colored_legend_scene.h" "completion_bar_widget.h" "donut_pie_widget.h" "double_slider_widget.h" + "driver_overrides_model.h" + "driver_overrides_notification_banner.h" + "driver_overrides_notification_config_widget.h" + "driver_overrides_tree_widget.h" "elided_line_label.h" "expanding_scroll_area.h" "file_loading_widget.h" @@ -38,6 +43,7 @@ file (GLOB CPP_INC "scaled_header_view.h" "scaled_label.h" "scaled_line_edit.h" + "scaled_link_label.h" "scaled_menu_bar.h" "scaled_push_button.h" "scaled_spin_box.h" @@ -61,18 +67,26 @@ file (GLOB CPP_INC # Add any .ui files file (GLOB UI_SRC + "banner_widget.ui" + "driver_overrides_notification_config_widget.ui" + "driver_overrides_tree_widget.ui" "message_overlay.ui" ) # Add all source files found within this directory. file (GLOB CPP_SRC "arrow_icon_combo_box.cpp" + "banner_widget.cpp" "check_box_widget.cpp" "colored_legend_graphics_view.cpp" "colored_legend_scene.cpp" "completion_bar_widget.cpp" "donut_pie_widget.cpp" "double_slider_widget.cpp" + "driver_overrides_model.cpp" + "driver_overrides_notification_banner.cpp" + "driver_overrides_notification_config_widget.cpp" + "driver_overrides_tree_widget.cpp" "elided_line_label.cpp" "expanding_scroll_area.cpp" "file_loading_widget.cpp" @@ -98,6 +112,7 @@ file (GLOB CPP_SRC "scaled_header_view.cpp" "scaled_label.cpp" "scaled_line_edit.cpp" + "scaled_link_label.cpp" "scaled_menu_bar.cpp" "scaled_push_button.cpp" "scaled_spin_box.cpp" diff --git a/source/qt_common/custom_widgets/banner_widget.cpp b/source/qt_common/custom_widgets/banner_widget.cpp new file mode 100644 index 0000000..21b11a3 --- /dev/null +++ b/source/qt_common/custom_widgets/banner_widget.cpp @@ -0,0 +1,137 @@ +//============================================================================= +// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +/// @author AMD Developer Tools Team +/// @file +/// @brief Implementation of the custom banner widget. +//============================================================================= + +#include "banner_widget.h" +#include "ui_banner_widget.h" + +#include + +// Static strings used for label widgets. +static const QString kBannerText = "[Notification message]"; +static const QString kCloseLink = "Close"; +static const QString kCloseText = QChar(0x2A2F); +static const QString kShowDetailsText = "See details"; +static const QString kShowDetailsLink = "ShowDetails"; +static const QString kDontShowAgainText = "Do not show again"; +static const QString kDontShowAgainLink = "DontShowAgain"; +static const QString kStyledButtonHtml = "%2"; +static const QString kCloseToolTip = "Close the notification banner."; + +BannerWidget::BannerWidget(QWidget* parent) + : QWidget(parent) + , ui_(new Ui::BannerWidget) +{ + ui_->setupUi(this); + Init(); +} + +BannerWidget::~BannerWidget() +{ +} + +void BannerWidget::Init() +{ + SetNotificationText(kBannerText); + ui_->show_details_label_->SetLink(kShowDetailsText, kShowDetailsLink); + ui_->dont_show_again_label_->SetLink(kDontShowAgainText, kDontShowAgainLink); + ui_->close_button_->setText(kStyledButtonHtml.arg(kCloseLink, kCloseText)); + ui_->close_button_->setWindowTitle(kCloseToolTip); + + connect(ui_->show_details_label_, &QLabel::linkActivated, this, &BannerWidget::HandleShowDetailsClicked); + connect(ui_->dont_show_again_label_, &QLabel::linkActivated, this, &BannerWidget::HandleDontShowAgainClicked); + connect(ui_->close_button_, &QLabel::linkActivated, this, &BannerWidget::HandleCloseClicked); + + // Install an event filter to handle propagating text color to the child labels. + installEventFilter(this); + + // Hide the banner until it is needed. + hide(); +} + +void BannerWidget::paintEvent(QPaintEvent* event) +{ + QWidget::paintEvent(event); + + QPainter painter(this); + + // Draw background for the button. + painter.setBrush(QBrush(palette().color(QPalette::Window))); + painter.drawRect(0, 0, width(), height()); +} + +bool BannerWidget::eventFilter(QObject* object, QEvent* event) +{ + const bool return_value = QWidget::eventFilter(object, event); + + if ((event->type() == QEvent::PaletteChange) || (event->type() == QEvent::Polish)) + { + // Propagate the text color to the child label widgets. + const QColor text_color = palette().color(QPalette::WindowText); + + // Iterate over all child widgets and set the text color for labels. + for (QObject* child : children()) + { + QLabel* label = qobject_cast(child); + if (label != nullptr) + { + // Get the label's palette and set the text color. + QPalette palette = label->palette(); + palette.setColor(QPalette::WindowText, text_color); + label->setPalette(palette); + } + } + } + + return return_value; +} + +void BannerWidget::HandleShowDetailsClicked(const QString& link) +{ + if (link == kShowDetailsLink) + { + emit ShowDetailsClicked(); + } +} + +void BannerWidget::SetNotificationText(const QString& notification_text) +{ + ui_->message_label_->setText(notification_text); +} + +void BannerWidget::HandleDontShowAgainClicked(const QString& link) +{ + if (link == kDontShowAgainLink) + { + DontShowAgainQuery(); + } +} + +void BannerWidget::HandleCloseClicked(const QString& link) +{ + if (link == kCloseLink) + { + hide(); + emit CloseClicked(); + } +} + +void BannerWidget::DontShowAgainQuery() +{ + emit DontShowAgainRequested(); +} + +void BannerWidget::SetLinkColor(const QColor& color) +{ + ui_->show_details_label_->SetLinkColor(color); + ui_->dont_show_again_label_->SetLinkColor(color); +} + +void BannerWidget::SetDisabledLinkColor(const QColor& color) +{ + ui_->show_details_label_->SetDisabledLinkColor(color); + ui_->dont_show_again_label_->SetDisabledLinkColor(color); +} diff --git a/source/qt_common/custom_widgets/banner_widget.h b/source/qt_common/custom_widgets/banner_widget.h new file mode 100644 index 0000000..02779d3 --- /dev/null +++ b/source/qt_common/custom_widgets/banner_widget.h @@ -0,0 +1,100 @@ +//============================================================================= +// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +/// @author AMD Developer Tools Team +/// @file +/// @brief Header for the custom banner widget. +//============================================================================= + +#ifndef QTCOMMON_CUSTOM_WIDGETS_BANNER_WIDGET_H_ +#define QTCOMMON_CUSTOM_WIDGETS_BANNER_WIDGET_H_ + +#include +#include + +namespace Ui +{ + class BannerWidget; +} + +/// @brief A custom widget for displaying a banner with a message and links. +class BannerWidget : public QWidget +{ + Q_OBJECT + +public: + /// @brief Constructor + /// + /// @param [in] parent The parent of the banner widget. + explicit BannerWidget(QWidget* parent = nullptr); + + /// @brief Destructor. + virtual ~BannerWidget(); + + /// @brief Set the text for the notification. + /// + /// @param [in] notification_text The text to display in the notification. + void SetNotificationText(const QString& notification_text); + + /// @brief Set text color used for links on the banner. + /// + /// @param [in] color The text color for the link. + void SetLinkColor(const QColor& color); + + /// @brief Set the text color used for disabled links on the banner. + /// + /// @param [in] color The text color for the disabled link. + void SetDisabledLinkColor(const QColor& color); + + /// @brief Emits a signal indicating that the user has clicked the "Do not show again" link. + virtual void DontShowAgainQuery(); + +protected: + /// @brief Overridden paint event for this widget. + /// + /// @param event The paint event + virtual void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE; + + /// @brief Event filter for the banner widget. + /// + /// Handles updating the text color for child labels. + /// + /// @param [in] object The object that the event is for. + /// @param [in] event The event that occurred. + /// + /// @return True if the event was handled, false otherwise. + bool eventFilter(QObject* object, QEvent* event); + +signals: + /// @brief Signal emitted when the "show details" link is clicked. + void ShowDetailsClicked(); + + /// @brief Signal emitted when the "Do not show again" link is clicked. + void DontShowAgainRequested(); + + /// @brief Signal emitted when the close button is clicked. + void CloseClicked(); + +private slots: + /// @brief Slot to handle the show details link being clicked. + /// + /// @param link The link that was clicked. + void HandleShowDetailsClicked(const QString& link); + + /// @brief Slot to handle the close button being clicked. + /// + /// @param link The link that was clicked. + void HandleCloseClicked(const QString& link); + + /// @brief Slot to handle the "don't show again" link being clicked. + /// + /// @param link The link that was clicked. + void HandleDontShowAgainClicked(const QString& link); + +private: + /// @brief Intializes the widget, adds subwidgets and inserts the banner into a parent layout if one exists. + void Init(); + +protected: + Ui::BannerWidget* ui_; ///< The Qt ui form. +}; +#endif // QTCOMMON_CUSTOM_WIDGETS_BANNER_WIDGET_H_ diff --git a/source/qt_common/custom_widgets/banner_widget.ui b/source/qt_common/custom_widgets/banner_widget.ui new file mode 100644 index 0000000..1f93d5a --- /dev/null +++ b/source/qt_common/custom_widgets/banner_widget.ui @@ -0,0 +1,174 @@ + + + BannerWidget + + + + 0 + 0 + 1054 + 32 + + + + + 0 + 0 + + + + Form + + + true + + + + 0 + + + 10 + + + 2 + + + 10 + + + 2 + + + + + + 0 + 0 + + + + + false + + + + [notification message] + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Fixed + + + + 60 + 10 + + + + + + + + + 0 + 0 + + + + + false + + + + See details + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 10 + + + + + + + + + 0 + 0 + + + + + false + + + + Do not show again + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Fixed + + + + 20 + 10 + + + + + + + + + 0 + 0 + + + + Close the notification banner. + + + X + + + + + + + + ScaledLabel + QLabel +
qt_common/custom_widgets/scaled_label.h
+
+ + ScaledLinkLabel + QLabel +
qt_common/custom_widgets/scaled_link_label.h
+
+
+ + + + +
diff --git a/source/qt_common/custom_widgets/check_box_widget.cpp b/source/qt_common/custom_widgets/check_box_widget.cpp index 7f3a236..646c4ce 100644 --- a/source/qt_common/custom_widgets/check_box_widget.cpp +++ b/source/qt_common/custom_widgets/check_box_widget.cpp @@ -127,3 +127,10 @@ void CheckBoxWidget::StateChanged(int checkbox_state) Q_ASSERT("Unsupported CheckBox state"); } } + +/// Setter for label_text property +void CheckBoxWidget::SetLabelText(const QString& text) +{ + SetOnText(text); + SetOffText(text); +} diff --git a/source/qt_common/custom_widgets/check_box_widget.h b/source/qt_common/custom_widgets/check_box_widget.h index 5ba36b1..04f3aaf 100644 --- a/source/qt_common/custom_widgets/check_box_widget.h +++ b/source/qt_common/custom_widgets/check_box_widget.h @@ -7,8 +7,8 @@ #ifndef QTCOMMON_CUSTOM_WIDGETS_CHECK_BOX_WIDGET_H_ #define QTCOMMON_CUSTOM_WIDGETS_CHECK_BOX_WIDGET_H_ -#include #include +#include /// Custom CheckBox which handles DPI scale changes, and assumes /// a 2:1 width:height ratio for the indicator. The size of the @@ -17,6 +17,13 @@ class CheckBoxWidget : public QCheckBox { Q_OBJECT + // This custom check box overrides the behavior of the associated text label. + // The standard setText() method is used to change the state of the checkbox if the + // text matches the ON or OFF text strings. In order to use the default behavior + // of the QCheckBox, set the "label_text" property to the desired string. This + // will change the ON and OFF text strings to the "label_text" value. + Q_PROPERTY(QString label_text READ text WRITE SetLabelText); + public: /// Explicit constructor /// \param parent The checkbox's parent @@ -37,6 +44,10 @@ class CheckBoxWidget : public QCheckBox /// If the font is changed programmatically, this should be called immediately after. void UpdateIndicatorSize(); + /// Setter for label_text property. + /// \param text - The new value for the text label. + void SetLabelText(const QString& text); + /// Override setText /// This ensures that the text of the button is properly updated, even if the /// developer enters default text in the .ui file. diff --git a/source/qt_common/custom_widgets/driver_overrides_model.cpp b/source/qt_common/custom_widgets/driver_overrides_model.cpp new file mode 100644 index 0000000..600de7f --- /dev/null +++ b/source/qt_common/custom_widgets/driver_overrides_model.cpp @@ -0,0 +1,823 @@ +//============================================================================= +// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +/// @author AMD Developer Tools Team +/// @file +/// @brief Implementation for the Driver Overrides Qt tree model. +/// +//============================================================================= + +#include "driver_overrides_model.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace driver_overrides +{ + // The Driver Overrides model singleton instance. + DriverOverridesModel* driver_overrides_model_instance_ = nullptr; + + // Driver Overrides JSON node names (for parsing imported JSON text). + static constexpr const char* kJsonNodeNameIsDriverExperiments = "IsDriverExperiments"; + static constexpr const char* kJsonNodeNameComponents = "Components"; + static constexpr const char* kJsonNodeNameStructures = "Structures"; + static constexpr const char* kJsonNodeNameSettingName = "SettingName"; + static constexpr const char* kJsonNodeNameSettingValue = "Value"; + static constexpr const char* kJsonNodeNameSettingDescription = "Description"; + static constexpr const char* kJsonNodeNameSettingValueType = "ValueType"; + + // Model section key names. + static constexpr const char* kSubTreeNameAttributes = "Attributes"; + static constexpr const char* kSubTreeNameOverridesTree = "OverridesTree"; + + // Formatted text strings for model attributes that are dynamically generated. + + // The message that will be displayed in the notification banner. The %1 argument will be replaced with the file type (e.g. "trace," "profile" or "scene"...). + static const QString kModelAttributeValueTextFormatBannerMessage = "The %1 was captured with at least one %2 active."; + + // The title of the message box that will be displayed when the user disables a notification. The %1 argument will be replaced with "Driver setting" or "Driver experiment." + static const QString kModelAttributeValueTextFormatDontShowMessageBoxTitle = "Disable %1 notification"; + + // The text that will be displayed in the message box when the user disables a notification. + static const QString kModelAttributeValueTextFormatDontShowMessageBoxText = + "Are you sure?\n\nThis notification can be restored in the General Settings pane."; + + // The title of the tree view that will be displayed in the Driver Overrides pane. The %1 argument will be replaced with "Driver setting" or "Driver experiment" (Note: the %1s is intentional to pluralize the string). + static const QString kModelAttributeValueTextFormatTreeViewTitle = "%1s"; + + // The title of the notification setting. The %1 argument will be replaced with the "Driver setting" or "Driver experiment" string. + static const QString kModelAttributeValueTextFormatNotificationSettingTitle = "%1 notification"; + + // The label of the notification setting. The %1 argument will be replaced with the file type (e.g. "trace," "profile" or "scene"...), + // and the %2 argument will be replaced with "Driver setting" or "Driver experiment." + static const QString kModelAttributeValueTextFormatNotificationSettingLabel = "Show a notification when loading a %1 with at least one %2 enabled"; + + // Static text strings. + static constexpr const char* kDriverSettingsText = "Driver setting"; + static constexpr const char* kDriverExperimentsText = "Driver experiment"; + static constexpr const char* kDefaultFileTypeText = "file"; + + // Boolean values as strings. + static constexpr const char* kBoolValueTrue = "True"; + static constexpr const char* kBoolValueFalse = "False"; + + // Converts tooltip to rich text so that it properly wordwraps. + static const QString kToolTipRichTextFormat = "%1"; + + /// @brief Convert a JSON value to a string. + /// + /// @param [in] json_value The JSON value to convert. + /// @param [out] out_value The converted value as a string. + /// + /// @return True if the conversion was successful, false otherwise. + static bool JsonObjectToString(const QJsonValue& json_value, QString& out_value) + { + bool result = true; + out_value.clear(); + + if (json_value.isBool()) + { + out_value = json_value.toBool() ? kBoolValueTrue : kBoolValueFalse; + } + else if (json_value.isDouble()) + { + out_value = QString::number(json_value.toDouble()); + } + else if (json_value.isString()) + { + out_value = json_value.toString(); + } + else + { + result = false; + } + + return result; + } + + // TreeItem implementation. + TreeItem::TreeItem(const QString& key, const QVariant& value, TreeItem* parent) + : key_(key) + , value_(value) + , parent_(parent) + , is_bold_(false) + { + } + + TreeItem::~TreeItem() + { + // Delete all child items owned by this item. + for (TreeItem* child : children_) + { + delete child; + } + } + + QString TreeItem::GetToolTip() const + { + return tool_tip_; + } + + void TreeItem::SetToolTip(const QString& tool_tip) + { + tool_tip_ = tool_tip; + } + + void TreeItem::SetIsBold(const bool is_bold) + { + is_bold_ = is_bold; + } + + bool TreeItem::IsBold() const + { + return is_bold_; + } + + void TreeItem::SetKey(const QString& key) + { + key_ = key; + } + + QString TreeItem::GetKey() const + { + return key_; + } + + void TreeItem::SetValue(const QVariant& value) + { + value_ = value; + } + + QVariant TreeItem::GetValue() const + { + return value_; + } + + void TreeItem::SetParent(TreeItem* parent) + { + parent_ = parent; + } + + TreeItem* TreeItem::GetParent() const + { + return parent_; + } + + void TreeItem::AddChild(TreeItem* child) + { + children_.append(child); + } + + TreeItem* TreeItem::GetChild(const int index) const + { + if (index >= 0 && index < children_.size()) + { + return children_.at(index); + } + return nullptr; + } + + int TreeItem::GetChildCount() const + { + return children_.size(); + } + + int TreeItem::GetRow() const + { + int row = 0; + if (parent_) + { + row = parent_->children_.indexOf(const_cast(this)); + } + + return row; + } + + bool TreeItem::RemoveAllChildren(TreeItem* parent) + { + bool result = false; + + if (parent != nullptr) + { + for (TreeItem* child : parent->children_) + { + delete child; + } + parent->children_.clear(); + result = true; + } + + return result; + } + + DriverOverridesModel* DriverOverridesModel::GetInstance() + { + if (driver_overrides_model_instance_ == nullptr) + { + driver_overrides_model_instance_ = new DriverOverridesModel(); + } + + return driver_overrides_model_instance_; + } + + void DriverOverridesModel::DestroyInstance() + { + delete driver_overrides_model_instance_; + driver_overrides_model_instance_ = nullptr; + } + + DriverOverridesModel::DriverOverridesModel() + { + // Create a new root item + TreeItem* root_item = new TreeItem("Root", "", nullptr); + SetRootItem(root_item); + + // Add the properties to the root branch of the tree model. + InitializeDefaultModelAttributes(); + + // Create a branch for the Driver Overrides sub-tree. + // The Driver Overrides sub-tree will contain the imported JSON Driver settings/Driver experiments. + AddOrUpdateChild(kSubTreeNameOverridesTree, "", RootIndex()); + } + + DriverOverridesModel::~DriverOverridesModel() + { + delete root_item_; + } + + QModelIndex DriverOverridesModel::index(int row, int column, const QModelIndex& parent) const + { + if (!hasIndex(row, column, parent)) + { + return QModelIndex(); + } + + TreeItem* parent_item = nullptr; + + if (!parent.isValid()) + { + parent_item = root_item_; + } + else + { + parent_item = static_cast(parent.internalPointer()); + } + + TreeItem* child_item = parent_item->GetChild(row); + if (child_item != nullptr) + { + return createIndex(row, column, child_item); + } + + return QModelIndex(); + } + + QModelIndex DriverOverridesModel::parent(const QModelIndex& index) const + { + if (!index.isValid()) + { + return QModelIndex(); + } + + TreeItem* child_item = static_cast(index.internalPointer()); + TreeItem* parent_item = child_item->GetParent(); + + if (parent_item == root_item_) + { + return QModelIndex(); + } + + return createIndex(parent_item->GetRow(), 0, parent_item); + } + + int DriverOverridesModel::rowCount(const QModelIndex& parent) const + { + if (root_item_ == nullptr) + { + return 0; + } + + TreeItem* parent_item = root_item_; + + if (parent.column() > 0) + { + return 0; + } + + if (!parent.isValid()) + { + parent_item = root_item_; + } + else + { + parent_item = static_cast(parent.internalPointer()); + } + + return parent_item->GetChildCount(); + } + + int DriverOverridesModel::columnCount(const QModelIndex& parent) const + { + Q_UNUSED(parent); + + // This model only has two columns: key and value. + return kModelMaxColumnCount; + } + + bool DriverOverridesModel::setData(const QModelIndex& index, const QVariant& value, int role) + { + if (index.isValid() && role == Qt::EditRole) + { + TreeItem* item = static_cast(index.internalPointer()); + item->SetValue(value); + emit dataChanged(index, index); + return true; + } + + return false; + } + + QVariant DriverOverridesModel::data(const QModelIndex& index, int role) const + { + if (!index.isValid()) + { + return QVariant(); + } + + const TreeItem* item = static_cast(index.internalPointer()); + + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + { + if (index.column() == kModelKeyColumnNumber) + { + return item->GetKey(); + } + else if (index.column() == kModelValueColumnNumber) + { + return item->GetValue(); + } + } + else if (role == Qt::FontRole) + { + if (item->IsBold()) + { + QFont bold_font(default_item_font_); + bold_font.setBold(true); + return bold_font; + } + else + { + return default_item_font_; + } + } + else if (role == DriverOverridesModel::custom_tooltip_role_) + { + // Note: A custom role is used to allow tooltips to only be displayed when the user hovers over the item text and not whitespace. + // This is necessary because the default tooltip role behavior displays the tooltip when the user hovers over any part of the tree view item. + // An event filter is needed to handle displaying the tooltip. The tooltip is formatted as rich text to force word wrapping. + return kToolTipRichTextFormat.arg(item->GetToolTip()); + } + + return QVariant(); + } + + void DriverOverridesModel::SetApplicationDetails(const QString& file_type_string, const bool is_driver_experiments) + { + SetModelAttributeValue(kModelAttributeNameApplicationFileTypeString, file_type_string); + SetModelAttributeValue(kModelAttributeNameIsDriverExperiments, is_driver_experiments); + UpdateModelAttributes(); + } + + void DriverOverridesModel::SetDefaultItemFont(const QFont& font) + { + default_item_font_ = font; + default_item_font_.setBold(false); + } + + void DriverOverridesModel::GetModelAttributes(DriverOverridesModelAttributes& out_attributes) const + { + out_attributes.show_notification = GetModelAttributeValue(kModelAttributeShowNotification).toBool(); + out_attributes.enable_notifications = GetModelAttributeValue(kModelAttributeNameEnableNotifications).toBool(); + out_attributes.driver_overrides_present = GetModelAttributeValue(kModelAttributeNameDriverOverridesPresent).toBool(); + out_attributes.is_driver_experiments = GetModelAttributeValue(kModelAttributeNameIsDriverExperiments).toBool(); + out_attributes.application_file_type_string = GetModelAttributeValue(kModelAttributeNameApplicationFileTypeString).toString(); + out_attributes.dont_show_message_box_text = GetModelAttributeValue(kModelAttributeNameDontShowMessageBoxText).toString(); + out_attributes.dont_show_message_box_title = GetModelAttributeValue(kModelAttributeNameDontShowMessageBoxTitle).toString(); + out_attributes.notification_message = GetModelAttributeValue(kModelAttributeNameNotificationMessage).toString(); + out_attributes.notification_setting_label = GetModelAttributeValue(kModelAttributeNameNotificationSettingLabel).toString(); + out_attributes.notification_setting_title = GetModelAttributeValue(kModelAttributeNameNotificationSettingTitle).toString(); + out_attributes.overrides_name = GetModelAttributeValue(kModelAttributeNameOverridesName).toString(); + out_attributes.tree_view_title = GetModelAttributeValue(kModelAttributeNameTreeViewTitle).toString(); + out_attributes.enable_see_details_link = GetModelAttributeValue(kModelAttributeEnableSeeDetailsLink).toBool(); + } + + QVariant DriverOverridesModel::GetModelAttributeValue(const QString& key) const + { + const QModelIndex& key_index = GetIndexForKey(key, GetModelAttributesSubTreeIndex()); + + if (key_index.isValid()) + { + // Use the key index to create a value index. + const QModelIndex& value_index = index(key_index.row(), kModelValueColumnNumber, key_index.parent()); + return value_index.data(); + } + + return QVariant(); + } + + bool DriverOverridesModel::SetModelAttributeValue(const QString& key, const QVariant& value) + { + bool result = false; + const QModelIndex& key_index = GetIndexForKey(key, GetModelAttributesSubTreeIndex()); + + if (key_index.isValid()) + { + if (key_index.isValid()) + { + // Use the key index to create a value index. + const QModelIndex& value_index = index(key_index.row(), kModelValueColumnNumber, key_index.parent()); + setData(value_index, value, Qt::EditRole); + result = true; + } + } + + return result; + } + + QModelIndex DriverOverridesModel::GetModelAttributesSubTreeIndex() const + { + return GetIndexForKey(kSubTreeNameAttributes, QModelIndex()); + } + + QModelIndex DriverOverridesModel::GetDriverOverridesSubTreeIndex() const + { + return GetIndexForKey(kSubTreeNameOverridesTree, QModelIndex()); + } + + bool DriverOverridesModel::BindWidgetToModelAttribute(const QString& model_attribute_name, + QWidget* widget, + const QString& widget_property, + QDataWidgetMapper* mapper) + { + bool result = false; + + if ((widget != nullptr) && (mapper != nullptr)) + { + const QModelIndex index = GetIndexForModelAttributeName(model_attribute_name); + if (index.isValid()) + { + mapper->setModel(this); + mapper->setOrientation(Qt::Orientation::Vertical); + mapper->setRootIndex(GetModelAttributesSubTreeIndex()); + mapper->addMapping(widget, index.row(), widget_property.toUtf8()); + mapper->setCurrentIndex(kModelValueColumnNumber); + mapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit); + result = true; + } + } + return result; + } + + void DriverOverridesModel::Reset() + { + // Clear the flags that indicates Driver Overrides are present. + SetModelAttributeValue(kModelAttributeNameDriverOverridesPresent, false); + SetModelAttributeValue(kModelAttributeShowNotification, false); + + // Remove the Driver Overrides tree items. + RemoveAllChildren(GetDriverOverridesSubTreeIndex()); + + // Update the model attributes based on the current model property values. + UpdateModelAttributes(); + } + + bool DriverOverridesModel::RemoveAllChildren(const QModelIndex& parent_index) + { + bool result = false; + const int row_count = rowCount(parent_index); + if (row_count > 0) + { + beginRemoveRows(parent_index, 0, row_count - 1); + TreeItem* parent_tree_item = static_cast(parent_index.internalPointer()); + parent_tree_item->RemoveAllChildren(parent_tree_item); + endRemoveRows(); + emit dataChanged(parent_index, parent_index); + result = true; + } + + return result; + } + + bool DriverOverridesModel::ImportFromJsonText(const QString& json_text) + { + bool is_success = true; + + // Parse the JSON text. + QJsonDocument json_doc = QJsonDocument::fromJson(json_text.toUtf8()); + QJsonObject json_object; + if (json_doc.isEmpty()) + { + is_success = false; + } + else + { + json_object = json_doc.object(); + if (json_object.isEmpty()) + { + is_success = false; + } + } + + const QModelIndex driver_overrides_tree_index = GetDriverOverridesSubTreeIndex(); + Q_ASSERT(driver_overrides_tree_index.isValid()); + + // Remove the existing Driver Overrides tree items if they already exist. + RemoveAllChildren(driver_overrides_tree_index); + + // Update the model with the parsed JSON data + bool is_driver_experiments_flag = false; + + if (json_object.contains(kJsonNodeNameIsDriverExperiments)) + { + QJsonValue is_driver_experiments = json_object.value(kJsonNodeNameIsDriverExperiments); + if (is_driver_experiments.isBool()) + { + is_driver_experiments_flag = is_driver_experiments.toBool(); + } + } + else + { + // If the JSON data does not contain the IsDriverExperiments flag, assume it is a Driver experiments. + is_driver_experiments_flag = true; + } + + SetModelAttributeValue(kModelAttributeNameIsDriverExperiments, is_driver_experiments_flag); + + // Check if Driver Experiments or Driver Settings are present in the JSON data. + if (json_object.contains(kJsonNodeNameStructures) || json_object.contains(kJsonNodeNameComponents)) + { + if (is_driver_experiments_flag) + { + is_success = ParseJsonStructureList(json_doc[kJsonNodeNameStructures].toObject(), driver_overrides_tree_index); + } + else + { + is_success = ParseJsonComponentList(json_doc[kJsonNodeNameComponents].toObject(), driver_overrides_tree_index); + } + + SetModelAttributeValue(kModelAttributeNameDriverOverridesPresent, is_success); + } + else + { + // If the JSON data does not contain any Driver settings or Driver experiments, set the Driver Overrides present attribute to false. + SetModelAttributeValue(kModelAttributeNameDriverOverridesPresent, false); + } + + // Update the Model Attributes based on the JSON data parsed. + UpdateModelAttributes(); + + // Emit the data changed signal to update the entire model. + const QModelIndex topLeft = index(0, 0); + const QModelIndex bottomRight = index(rowCount() - 1, columnCount() - 1); + emit dataChanged(topLeft, bottomRight); + + emit DriverOverridesImported(); + + return is_success; + } + + void DriverOverridesModel::InitializeDefaultModelAttributes() + { + // Add the attribute item root branch to the tree model. + const QModelIndex attribute_index = AddOrUpdateChild(kSubTreeNameAttributes, "", RootIndex()); + + // Add default parameters to the model. + AddOrUpdateChild(kModelAttributeNameEnableNotifications, true, attribute_index); + AddOrUpdateChild(kModelAttributeShowNotification, false, attribute_index); + AddOrUpdateChild(kModelAttributeNameApplicationFileTypeString, kDefaultFileTypeText, attribute_index); + AddOrUpdateChild(kModelAttributeNameDriverOverridesPresent, false, attribute_index); + AddOrUpdateChild(kModelAttributeNameIsDriverExperiments, true, attribute_index); + AddOrUpdateChild(kModelAttributeShowNotification, false, attribute_index); + AddOrUpdateChild(kModelAttributeNameOverridesName, "", attribute_index); + AddOrUpdateChild(kModelAttributeNameNotificationMessage, "", attribute_index); + AddOrUpdateChild(kModelAttributeNameDontShowMessageBoxTitle, "", attribute_index); + AddOrUpdateChild(kModelAttributeNameDontShowMessageBoxText, "", attribute_index); + AddOrUpdateChild(kModelAttributeNameTreeViewTitle, "", attribute_index); + AddOrUpdateChild(kModelAttributeNameNotificationSettingTitle, "", attribute_index); + AddOrUpdateChild(kModelAttributeNameNotificationSettingLabel, "", attribute_index); + AddOrUpdateChild(kModelAttributeEnableSeeDetailsLink, true, attribute_index); + + // Update the attributes that are dynamically generated based on other attribute values. + UpdateModelAttributes(); + } + + bool DriverOverridesModel::ParseJsonComponentList(const QJsonObject& json_object, const QModelIndex& parent_index) + { + bool is_success = false; + for (auto components_iterator = json_object.constBegin(); components_iterator != json_object.constEnd(); ++components_iterator) + { + QString component_key = components_iterator.key(); + QModelIndex index = AddOrUpdateChild(component_key, "", parent_index); + SetItemBold(index, true); + is_success = ParseJsonStructureList(components_iterator.value().toObject().value(kJsonNodeNameStructures).toObject(), index); + + if (!is_success) + { + break; + } + } + + return is_success; + } + + bool DriverOverridesModel::ParseJsonStructureList(const QJsonObject& json_object, const QModelIndex& parent_index) + { + bool is_success = false; + + for (auto structures_iterator = json_object.constBegin(); structures_iterator != json_object.constEnd(); ++structures_iterator) + { + QString structure_key = structures_iterator.key(); + QModelIndex index = AddOrUpdateChild(structure_key, "", parent_index); + SetItemBold(index, true); + is_success = ParseJsonSettingList(structures_iterator.value().toArray(), index); + + if (!is_success) + { + break; + } + } + + return is_success; + } + + bool DriverOverridesModel::ParseJsonSettingList(const QJsonArray& json_settings_array, const QModelIndex& parent_index) + { + bool is_success = false; + + for (auto settings_iterator = json_settings_array.constBegin(); settings_iterator != json_settings_array.constEnd(); ++settings_iterator) + { + is_success = ParseJsonSetting(settings_iterator->toObject(), parent_index); + + if (!is_success) + { + break; + } + } + + return is_success; + } + + bool DriverOverridesModel::ParseJsonSetting(const QJsonObject& json_settings_object, const QModelIndex& parent_index) + { + bool is_success = false; + if (!json_settings_object.isEmpty()) + { + if (json_settings_object.contains(kJsonNodeNameSettingName) && json_settings_object.contains(kJsonNodeNameSettingValue)) + { + QString setting_name = json_settings_object.value(kJsonNodeNameSettingName).toString(); + QString setting_value; + is_success = JsonObjectToString(json_settings_object.value(kJsonNodeNameSettingValue), setting_value); + + if (is_success) + { + // Add the setting name and value to the model. + QModelIndex index = AddOrUpdateChild(setting_name, setting_value, parent_index); + + // Check for an optional setting description. + if (json_settings_object.contains(kJsonNodeNameSettingDescription)) + { + // Use the description for the setting's tooltip. + QString setting_description = json_settings_object.value(kJsonNodeNameSettingDescription).toString(); + SetItemToolTip(index, setting_description); + } + } + } + } + + return is_success; + } + + void DriverOverridesModel::UpdateModelAttributes() + { + // Update the dynamic model attributes based on the current model values. + const bool is_driver_experiments = GetModelAttributeValue(kModelAttributeNameIsDriverExperiments).toBool(); + const bool is_present = GetModelAttributeValue(kModelAttributeNameDriverOverridesPresent).toBool(); + const bool is_enabled = GetModelAttributeValue(kModelAttributeNameEnableNotifications).toBool(); + const QString& driver_overrides_file_type = GetModelAttributeValue(kModelAttributeNameApplicationFileTypeString).toString(); + + QString driver_overrides_name; + driver_overrides_name = is_driver_experiments ? kDriverExperimentsText : kDriverSettingsText; + + SetModelAttributeValue(kModelAttributeShowNotification, is_enabled && is_present); + SetModelAttributeValue(kModelAttributeNameOverridesName, driver_overrides_name); + SetModelAttributeValue(kModelAttributeNameNotificationMessage, + kModelAttributeValueTextFormatBannerMessage.arg(driver_overrides_file_type).arg(driver_overrides_name)); + SetModelAttributeValue(kModelAttributeNameDontShowMessageBoxTitle, kModelAttributeValueTextFormatDontShowMessageBoxTitle.arg(driver_overrides_name)); + SetModelAttributeValue(kModelAttributeNameDontShowMessageBoxText, kModelAttributeValueTextFormatDontShowMessageBoxText); + SetModelAttributeValue(kModelAttributeNameTreeViewTitle, kModelAttributeValueTextFormatTreeViewTitle.arg(driver_overrides_name)); + SetModelAttributeValue(kModelAttributeNameNotificationSettingTitle, kModelAttributeValueTextFormatNotificationSettingTitle.arg(driver_overrides_name)); + SetModelAttributeValue(kModelAttributeNameNotificationSettingLabel, + kModelAttributeValueTextFormatNotificationSettingLabel.arg(driver_overrides_file_type).arg(driver_overrides_name)); + } + + void DriverOverridesModel::SetItemToolTip(const QModelIndex& index, const QString& tool_tip) + { + if (index.isValid()) + { + TreeItem* item = static_cast(index.internalPointer()); + item->SetToolTip(tool_tip); + } + } + + void DriverOverridesModel::SetItemBold(const QModelIndex& index, const bool is_bold) + { + if (index.isValid()) + { + TreeItem* item = static_cast(index.internalPointer()); + item->SetIsBold(is_bold); + } + } + + void DriverOverridesModel::SetRootItem(TreeItem* root_item) + { + root_item_ = root_item; + } + + QModelIndex DriverOverridesModel::RootIndex() const + { + return createIndex(0, 0, root_item_); + } + + QModelIndex DriverOverridesModel::GetIndexForKey(const QString& key, const QModelIndex& parent) const + { + QModelIndex new_index; + TreeItem* parent_item = nullptr; + + if (!parent.isValid()) + { + parent_item = root_item_; + } + else + { + parent_item = static_cast(parent.internalPointer()); + } + + for (int i = 0; i < parent_item->GetChildCount(); i++) + { + TreeItem* child_item = parent_item->GetChild(i); + if (child_item != nullptr && child_item->GetKey() == key) + { + new_index = createIndex(i, 0, child_item); + break; + } + } + + return new_index; + } + + QModelIndex DriverOverridesModel::GetIndexForModelAttributeName(const QString& attribute_name) const + { + return GetIndexForKey(attribute_name, GetModelAttributesSubTreeIndex()); + } + + QModelIndex DriverOverridesModel::AddOrUpdateChild(const QString& key, const QVariant& value, const QModelIndex& parent) + { + TreeItem* parent_item = static_cast(parent.internalPointer()); + TreeItem* item = AddOrUpdateChildItem(key, value, parent_item); + + return createIndex(item->GetRow(), 0, item); + } + + TreeItem* DriverOverridesModel::AddOrUpdateChildItem(const QString& key, const QVariant& value, TreeItem* parent) + { + TreeItem* item = nullptr; + + // Search for a child item with the same key name. If it exists, return the existing TreeItem pointer. + for (int i = 0; i < parent->GetChildCount(); i++) + { + TreeItem* child_item = parent->GetChild(i); + if (child_item != nullptr && child_item->GetKey() == key) + { + item = child_item; + item->SetValue(value); + } + } + + // If the child item does not exist, create a new TreeItem and add it to the parent. + if (item == nullptr) + { + item = new TreeItem(key, value, parent); + parent->AddChild(item); + } + + return item; + } +} // namespace driver_overrides diff --git a/source/qt_common/custom_widgets/driver_overrides_model.h b/source/qt_common/custom_widgets/driver_overrides_model.h new file mode 100644 index 0000000..27adb65 --- /dev/null +++ b/source/qt_common/custom_widgets/driver_overrides_model.h @@ -0,0 +1,440 @@ +//============================================================================= +// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +/// @author AMD Developer Tools Team +/// @file +/// @brief Header file for the Driver Overrides Qt tree model. +/// +/// A Qt tree model that is used to display JSON data from the Driver Overrides RDF chunck. +/// The model is divided into two main sections: Model Attributes and a Driver Overrides Tree. +/// The Driver Overrides Tree section contains the JSON data from the Driver Overrides RDF chunck. +/// The Attributes section contains the Driver Override properties. Some of which are updated based +/// on the JSON data. The model attributes are mapped to the UI widgets using a QDataWidgetMapper and +/// determine the behavior of the UI (e.g., whether to show a notification when Driver Overrides are +/// present and what message to display). The data in the model can be accessed using the standard +/// QAbstractItemModel interface. In addition, attributes can be set and retrieved using the +/// SetModelAttributeValue() and GetModelAttributeValue() methods. The model can be updated with JSON +/// data from the RDF DriverOverrides chunk using the ImportFromJsonText() method. +/// +/// Note: When displaying the Driver Overrides tree with a QTreeView based widget, +/// The view root node should be set to the index returned by GetDriverOverridesSubTreeIndex(). +/// Otherwise, the tree will display the model attributes as well as the Driver Overrides tree. +//============================================================================= + +#ifndef QTCOMMON_CUSTOM_WIDGETS_DRIVER_OVERRIDES_MODEL_H_ +#define QTCOMMON_CUSTOM_WIDGETS_DRIVER_OVERRIDES_MODEL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace driver_overrides +{ + // The column numbers for the model. + static constexpr const int kModelKeyColumnNumber = 0; + static constexpr const int kModelValueColumnNumber = 1; + static constexpr const int kModelMaxColumnCount = 2; + + // Model attribute names. Used to retrieve or set the value of a specific attribute in the model. + static constexpr const char* kModelAttributeNameApplicationFileTypeString = "ApplicationFileTypeString"; + static constexpr const char* kModelAttributeNameOverridesName = "OverridesName"; + static constexpr const char* kModelAttributeNameNotificationMessage = "NotificationMessage"; + static constexpr const char* kModelAttributeNameDontShowMessageBoxTitle = "DontShowMessageBoxTitle"; + static constexpr const char* kModelAttributeNameDontShowMessageBoxText = "DontShowMessageBoxText"; + static constexpr const char* kModelAttributeNameTreeViewTitle = "TreeViewTitle"; + static constexpr const char* kModelAttributeNameNotificationSettingTitle = "NotificationSettingTitle"; + static constexpr const char* kModelAttributeNameNotificationSettingLabel = "NotificationSettingLabel"; + static constexpr const char* kModelAttributeShowNotification = "ShowNotification"; + static constexpr const char* kModelAttributeNameEnableNotifications = "EnableNotifications"; + static constexpr const char* kModelAttributeNameDriverOverridesPresent = "DriverOverridesPresent"; + static constexpr const char* kModelAttributeNameIsDriverExperiments = "IsDriverExperiments"; + static constexpr const char* kModelAttributeEnableSeeDetailsLink = "EnableSeeDetailsLink"; + + typedef struct DriverOverridesModelAttributes + { + QString application_file_type_string; ///< The name of the file type that the application loads. + QString overrides_name; ///< The name of the driver overrides (either "driver setting" or "driver experiment"). + QString notification_message; ///< The message to display in the notification if driver overrides are present in the loaded file. + QString dont_show_message_box_title; ///< The title of the message box that asks the user if they want to show the notification again. + QString dont_show_message_box_text; ///< The text of the message box that asks the user if they want to show the notification again. + QString tree_view_title; ///< The title of the tree view ("Driver settings" or "Driver experiments"). + QString notification_setting_title; ///< The title above the show notification setting checkbox. + QString notification_setting_label; ///< The label for the show notification setting checkbox. + bool show_notification; ///< True if the driver override notification should be shown, false otherwise when a file is loaded. + bool enable_notifications; ///< True if the driver override notification is enabled, false otherwise. + bool driver_overrides_present; ///< True if driver overrides are present in the loaded file, false otherwise. + bool is_driver_experiments; ///< True if the loaded file contains driver experiments, false otherwise. + bool enable_see_details_link; ///< True if the "See Details" link should be enabled, false otherwise. + } DriverOverridesModelAttributes; + + /// @brief A class that represent a tree item in the model. + class TreeItem + { + public: + /// @brief Constructor. + /// + /// @param [in] key The key name for the item. + /// @param [in] value The value for the item. + /// @param [in] parent The parent of this item. + TreeItem(const QString& key, const QVariant& value, TreeItem* parent = nullptr); + + /// @brief Destructor. + ~TreeItem(); + + /// @brief Set the tool tip for the item. + /// + /// @param [in] tool_tip The tool tip string. + void SetToolTip(const QString& tool_tip); + + /// @brief Get tool tip for the item. + /// + /// @return The tool tip string. + QString GetToolTip() const; + + /// @brief Set the bold flag of the font used for this item. + /// + /// @param [in] is_bold True if the font should be bold, false otherwise. + void SetIsBold(const bool is_bold); + + /// @brief Check if the item is bold. + /// + /// @return True if the item is bold, false otherwise. + bool IsBold() const; + + /// @brief Get the key name for the item. + /// + /// @return The key name. + QString GetKey() const; + + /// @brief Get the value for the item. + /// + /// @return The value. + QVariant GetValue() const; + + /// @brief Get the parent of the item. + /// + /// @return The parent. + TreeItem* GetParent() const; + + /// @brief Get the child at the specified index. + /// + /// @param [in] index The row index of the child. + /// + /// @return The child item. + TreeItem* GetChild(const int index) const; + + /// @brief Get the number of children for this item. + /// + /// @return The number of children. + int GetChildCount() const; + + /// @brief Get the row number of this item. + /// + /// @return The row number. + int GetRow() const; + + /// @brief Set the key name for the item. + /// + /// @param [in] key The key name. + void SetKey(const QString& key); + + /// @brief Set the value for the item. + /// + /// @param [in] value The value. + void SetValue(const QVariant& value); + + /// @brief Set the parent of the item. + /// + /// @param [in] parent The parent item. + void SetParent(TreeItem* parent); + + /// @brief Add a child to this item. + /// + /// @param [in] child The child item. + void AddChild(TreeItem* child); + + /// @brief Remove all children from this item. + /// + /// @param [in] parent The parent of the item. + /// + /// @return True if children were removed, false otherwise. + bool RemoveAllChildren(TreeItem* parent); + + private: + QString key_; ///< The key name for this item. + QVariant value_; ///< The value for this item. + QString tool_tip_; ///< The tool tip for this item. + TreeItem* parent_; ///< The parent of this item. + QList children_; ///< A list of the children for this item. + bool is_bold_; ///< Flag to indicate if the item should be bold. + }; + + /// @brief A model that translates Driver Overrides JSON text into data viewable by a QTreeView. + class DriverOverridesModel : public QAbstractItemModel + { + Q_OBJECT + + public: + /// @brief Retrieve the singleton instance of the model. + /// + /// @return A pointer to the singleton instance of the model. + static DriverOverridesModel* GetInstance(); + + /// @brief Destroy the singleton instance of the model. + static void DestroyInstance(); + + /// @brief Remove the Driver Overrides sub-tree and resets model attributes. + void Reset(); + + /// @brief Imports the Driver Overrides JSON data into the model. + /// + /// @param [in] json_text The JSON data to import into the model. + /// + /// @return True if the JSON data was successfully imported, false otherwise. + bool ImportFromJsonText(const QString& json_text); + + /// @brief Retrieves the Model Attributes and copies them to a structure. + /// + /// @param [out] out_attributes The structure to copy the model attributes to. + void GetModelAttributes(DriverOverridesModelAttributes& out_attributes) const; + + /// @brief Set the default font for the items in the model (retrieved from the Data() method with Font role). + /// + /// @param [in] font The font to use. + void SetDefaultItemFont(const QFont& font); + + /// @brief Retrieves the index of the Model Attributes sub-tree. + /// + /// @return The index of the Model Attributes sub-tree. + QModelIndex GetModelAttributesSubTreeIndex() const; + + /// @brief Retrieves the index of the Driver Overrides sub-tree. + /// + /// @return The index of the Driver Overrides sub tree. + QModelIndex GetDriverOverridesSubTreeIndex() const; + + /// @brief Maps a widget to an attribute in the model. + /// + /// @param [in] key The name of the attribute in the model. + /// @param [in] widget The widget to bind to the attribute. + /// @param [in] widget_property The property of the widget to bind to. + /// @param [in] mapper The data widget mapper to use. + /// + /// @return True if the widget was successfully bound to a Model attribute , false otherwise. + bool BindWidgetToModelAttribute(const QString& key, QWidget* widget, const QString& widget_property, QDataWidgetMapper* mapper); + + /// @brief Sets the value of the Model Attribute with the given name. + /// + /// @param [in] key The name of the Model Attribute to set the value for. + /// @param [in] value The value to set the attribute to. + /// + /// @return True if the attribute was successfully set, false otherwise. + bool SetModelAttributeValue(const QString& key, const QVariant& value); + + /// @brief Retrieves the value of a Model Attribute with the given name. + /// + /// @param [in] key The name of the attribute to retrieve the value for. + /// + /// @return The value of the attribute with the given name. The return value is marked invalid if the attribute name doesn't exist. + QVariant GetModelAttributeValue(const QString& key) const; + + /// @brief Sets details about the host application. + /// + /// The application file type string is the name of the file type that the application loads. + /// It is used to customize Model Attributes which contain the application file type string. + /// + /// @param [in] file_type_string The name of the file type that the application loads. + /// @param [in] is_driver_experiments True if the application defaults to using Driver Experiments, false otherwise. + void SetApplicationDetails(const QString& file_type_string, const bool is_driver_experiments = true); + + /// @brief Get an index from the model. + /// + /// @param [in] row The row of the parent in the model. + /// @param [in] column The column of the parent in the model. + /// @param [in] parent The parent index. + /// + /// @return The model index. + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE; + + /// @brief Get the parent index for the model. + /// + /// @param [in] index The model index to retrieve the parent for. + /// + /// @return The parent index. + QModelIndex parent(const QModelIndex& index) const Q_DECL_OVERRIDE; + + /// @brief Get the row count for the model. + /// + /// @param [in] parent The parent index of the row count to retrieve. + /// + /// @return The row count. + int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE; + + /// @brief Return the column count for the model. + /// + /// @param [in] parent The parent index. + /// + /// @return The column count. + int columnCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE; + + /// @brief Get data from the model. + /// + /// @param [in] index The model index. + /// @param [in] role The role. + /// + /// @return The data. + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + + /// @brief Set the data for the model. + /// + /// @param [in] index The model index. + /// @param [in] value The value. + /// @param [in] role The role. + /// + /// @return True if the data was set, false otherwise. + bool setData(const QModelIndex& index, const QVariant& value, int role) Q_DECL_OVERRIDE; + + static const int custom_tooltip_role_ = Qt::UserRole + 1; ///< Custom role for the tool tip. + + signals: + /// @brief Signal emitted when the driver overrides have been imported. + void DriverOverridesImported(); + + private: + /// @brief Constructor (made private since this is a singleton). + DriverOverridesModel(); + + /// @brief Move Constructor (deleted since this is a singleton). + DriverOverridesModel(const DriverOverridesModel&&) = delete; + + /// @brief Destructor (made private since this is a singleton). + ~DriverOverridesModel(); + + /// @brief Copy constructor (deleted since this is a singleton). + DriverOverridesModel(const DriverOverridesModel&) = delete; + + /// @brief Assignment operator (deleted since this is a singleton). + /// + /// @return A reference to the assigned object. + DriverOverridesModel& operator=(const DriverOverridesModel&) = delete; + + /// @brief Move Assignment operator (deleted since this is a singleton). + /// + /// @return A reference to the assigned object. + DriverOverridesModel& operator=(const DriverOverridesModel&&) = delete; + + /// @brief Initializes the model with default attributes. + void InitializeDefaultModelAttributes(); + + /// @brief Set the root item for the model. + /// + /// @param [in] root_item The root item. + void SetRootItem(TreeItem* root_item); + + /// @brief Retrieve the root index of the model. + /// + /// @return The root index. + QModelIndex RootIndex() const; + + /// @brief Updates Model Attributes based on the Driver Overrides JSON data. + void UpdateModelAttributes(); + + /// @brief Retrieves the index of the Model Attribute with the given name. + /// + /// @param [in] attribute_name The name of the attribute to retrieve the index for. + /// + /// @return The index of the attribute with the given name or invalid index if the attribute doesn't exist. + QModelIndex GetIndexForModelAttributeName(const QString& attribute_name) const; + + /// @brief Get the model index for the specified key. + /// + /// @param [in] key The key name. + /// @param [in] parent The parent index. + /// + /// @return The model index. + QModelIndex GetIndexForKey(const QString& key, const QModelIndex& parent) const; + + /// @brief Add a child to the model. + /// + /// If a child item with the same key already exists, the value of the existing item will be updated. + /// + /// @param [in] key The key name of the item. + /// @param [in] value The value of the item. + /// @param [in] parent The parent index that the child item is being added to. + /// + /// @return The model index of the child. + QModelIndex AddOrUpdateChild(const QString& key, const QVariant& value, const QModelIndex& parent); + + /// @brief Add a child item to the model. + /// + /// If a child item with the same key already exists, the value of the existing item will be updated. + /// + /// @param [in] name The key name of the item. + /// @param [in] value The value of the item. + /// @param [in] parent The parent item. + /// + /// @return The new child item. + TreeItem* AddOrUpdateChildItem(const QString& name, const QVariant& value, TreeItem* parent); + + /// @brief Set tool tip for an item in the model. + /// + /// @param [in] index The model index. + /// @param [in] tool_tip The tool tip string. + void SetItemToolTip(const QModelIndex& index, const QString& tool_tip); + + /// @brief Set the bold flag of the font used for an item in the model. + /// + /// @param [in] index The model index. + /// @param [in] is_bold True if the font should be bold, false otherwise. + void SetItemBold(const QModelIndex& index, const bool is_bold); + + /// @brief Remove all children of the parent from the model. + /// + /// @param [in] parent The parent index. + /// + /// @return True if children were removed, false otherwise. + bool RemoveAllChildren(const QModelIndex& parent); + + /// @brief Parse the JSON Component list and add it to the model. + /// + /// @param [in] json_object The JSON object to parse. + /// @param [in] parent_index The parent index. + /// + /// @return True if the JSON Component list was successfully parsed and added to the model, false otherwise. + bool ParseJsonComponentList(const QJsonObject& json_object, const QModelIndex& parent_index); + + /// @brief Parse the JSON Structure list and add it to the model. + /// + /// @param [in] json_object The JSON object to parse. + /// @param [in] parent_index The parent index. + /// + /// @return True if the JSON Structure list was successfully parsed and added to the model, false otherwise. + bool ParseJsonStructureList(const QJsonObject& json_object, const QModelIndex& parent_index); + + /// @brief Parse the JSON Setting list and add it to the model. + /// + /// @param [in] json_settings_array The JSON array of settings to parse. + /// @param [in] parent_index The parent index. + /// + /// @return True if the JSON Setting list was successfully parsed and added to the model, false otherwise. + bool ParseJsonSettingList(const QJsonArray& json_settings_array, const QModelIndex& parent_index); + + /// @brief Parse the JSON Setting and add it to the model. + /// + /// @param [in] json_settings_object The JSON object of the setting to parse. + /// @param [in] parent_index The parent index. + /// + /// @return True if the JSON Setting was successfully parsed and added to the model, false otherwise. + bool ParseJsonSetting(const QJsonObject& json_settings_object, const QModelIndex& parent_index); + + private: + TreeItem* root_item_; ///< The root item of the model. + QFont default_item_font_; ///< The default font for the items in the model. + }; +} // namespace driver_overrides +#endif // QTCOMMON_CUSTOM_WIDGETS_DRIVER_OVERRIDES_MODEL_H_ diff --git a/source/qt_common/custom_widgets/driver_overrides_notification_banner.cpp b/source/qt_common/custom_widgets/driver_overrides_notification_banner.cpp new file mode 100644 index 0000000..e54b01a --- /dev/null +++ b/source/qt_common/custom_widgets/driver_overrides_notification_banner.cpp @@ -0,0 +1,78 @@ +//============================================================================= +// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +/// @author AMD Developer Tools Team +/// @file +/// @brief Implementation of the custom driver overrides notification banner. +//============================================================================= + +#include "driver_overrides_notification_banner.h" +#include "ui_banner_widget.h" + +#include "qt_util.h" +#include "scaled_label.h" + +#include "driver_overrides_model.h" + +DriverOverridesNotificationBanner::DriverOverridesNotificationBanner(QWidget* parent) + : BannerWidget(parent) +{ + Init(); +} + +DriverOverridesNotificationBanner::~DriverOverridesNotificationBanner() +{ +} + +void DriverOverridesNotificationBanner::Init() +{ + // Map the dynamic model properties to the UI elements. + driver_overrides::DriverOverridesModel::GetInstance()->BindWidgetToModelAttribute( + driver_overrides::kModelAttributeShowNotification, this, "visible", &banner_message_mapper_); + driver_overrides::DriverOverridesModel::GetInstance()->BindWidgetToModelAttribute( + driver_overrides::kModelAttributeNameNotificationMessage, ui_->message_label_, "text", &banner_message_mapper_); + driver_overrides::DriverOverridesModel::GetInstance()->BindWidgetToModelAttribute( + driver_overrides::kModelAttributeEnableSeeDetailsLink, ui_->show_details_label_, "enabled", &see_details_link_mapper_); +} + +void DriverOverridesNotificationBanner::DontShowAgainQuery() +{ + // Get the title and message text from the model. + driver_overrides::DriverOverridesModel* driver_overrides_model = driver_overrides::DriverOverridesModel::GetInstance(); + const QString& title_text = driver_overrides_model->GetModelAttributeValue(driver_overrides::kModelAttributeNameDontShowMessageBoxTitle).toString(); + const QString& message_text = driver_overrides_model->GetModelAttributeValue(driver_overrides::kModelAttributeNameDontShowMessageBoxText).toString(); + + // Show a message box to confirm the user wants to disable notifications. + const int response = QtCommon::QtUtils::ShowMessageBox(this, QMessageBox::Yes | QMessageBox::No, QMessageBox::Warning, title_text, message_text); + + if (response == QMessageBox::Yes) + { + // Update the model to disable notifications. + driver_overrides_model->SetModelAttributeValue(driver_overrides::kModelAttributeNameEnableNotifications, false); + driver_overrides_model->SetModelAttributeValue(driver_overrides::kModelAttributeShowNotification, false); + + // Emit the signal to notify the application that the persistant setting needs to be updated. + emit DontShowAgainRequested(); + } +} + +QColor DriverOverridesNotificationBanner::GetLinkColor() const +{ + return link_color_; +} + +QColor DriverOverridesNotificationBanner::GetDisabledLinkColor() const +{ + return disabled_link_color_; +} + +void DriverOverridesNotificationBanner::SetLinkColor(const QColor color) +{ + link_color_ = color; + BannerWidget::SetLinkColor(color); +} + +void DriverOverridesNotificationBanner::SetDisabledLinkColor(const QColor color) +{ + disabled_link_color_ = color; + BannerWidget::SetDisabledLinkColor(color); +} diff --git a/source/qt_common/custom_widgets/driver_overrides_notification_banner.h b/source/qt_common/custom_widgets/driver_overrides_notification_banner.h new file mode 100644 index 0000000..6398784 --- /dev/null +++ b/source/qt_common/custom_widgets/driver_overrides_notification_banner.h @@ -0,0 +1,65 @@ +//============================================================================= +// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +/// @author AMD Developer Tools Team +/// @file +/// @brief Header for the custom driver overrides notification banner. +//============================================================================= + +#ifndef QTCOMMON_CUSTOM_WIDGETS_DRIVER_OVERRIDES_NOTIFICATION_BANNER_H_ +#define QTCOMMON_CUSTOM_WIDGETS_DRIVER_OVERRIDES_NOTIFICATION_BANNER_H_ + +#include "banner_widget.h" + +#include + +/// @brief Custom widget for the driver overrides notification banner. +class DriverOverridesNotificationBanner : public BannerWidget +{ + Q_OBJECT + Q_PROPERTY(QColor linkColor READ GetLinkColor WRITE SetLinkColor) + Q_PROPERTY(QColor disabledLinkColor READ GetDisabledLinkColor WRITE SetDisabledLinkColor) + +public: + /// @brief Constructor + /// + /// @param [in] parent The parent of the banner widget. + explicit DriverOverridesNotificationBanner(QWidget* parent); + + /// @brief Destructor. + virtual ~DriverOverridesNotificationBanner(); + + /// @brief Show a message box to confirm the user wants to disable notifications. + virtual void DontShowAgainQuery(); + + /// @brief Get the link text color for the labels. + /// + /// @return The link text color. + QColor GetLinkColor() const; + + /// @brief Get the disabled link text color for the labels. + /// + /// @return The disabled link text color. + QColor GetDisabledLinkColor() const; + + /// @brief Set the link text color for the labels. + /// + /// @param [in] color The color to set. + void SetLinkColor(const QColor color); + + /// @brief Set the disabled link text color for the labels. + /// + /// @param [in] color The color to set. + void SetDisabledLinkColor(const QColor color); + +private: + /// @brief Intializes the widget. + void Init(); + +private: + QDataWidgetMapper banner_message_mapper_; ///< Data widget mapper for binding model properties to banner UI elements. + QDataWidgetMapper see_details_link_mapper_; ///< Data widget mapper for binding model enable property to the "see details" link. + + QColor link_color_; ///< The text color for links. + QColor disabled_link_color_; ///< The text color for disabled links. +}; +#endif // QTCOMMON_CUSTOM_WIDGETS_DRIVER_OVERRIDES_NOTIFICATION_BANNER_H_ diff --git a/source/qt_common/custom_widgets/driver_overrides_notification_config_widget.cpp b/source/qt_common/custom_widgets/driver_overrides_notification_config_widget.cpp new file mode 100644 index 0000000..f666393 --- /dev/null +++ b/source/qt_common/custom_widgets/driver_overrides_notification_config_widget.cpp @@ -0,0 +1,92 @@ +//============================================================================= +// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +/// @author AMD Developer Tools Team +/// @file +/// @brief Implementation of the custom Driver Overrides notification configuration widget. +//============================================================================= + +#include "driver_overrides_notification_config_widget.h" +#include "ui_driver_overrides_notification_config_widget.h" + +#include "driver_overrides_model.h" +#include "scaled_label.h" + +#include + +DriverOverridesNotificationConfigWidget::DriverOverridesNotificationConfigWidget(QWidget* parent) + : QWidget(parent) + , ui_(new Ui::DriverOverridesNotificationConfigWidget) +{ + ui_->setupUi(this); +} + +DriverOverridesNotificationConfigWidget::~DriverOverridesNotificationConfigWidget() +{ +} + +bool DriverOverridesNotificationConfigWidget::Init(const bool check_box_state, const bool include_on_off_indicator) +{ + bool result = true; + auto driver_overrides_model = driver_overrides::DriverOverridesModel::GetInstance(); + QWidget* check_box_label_widget = nullptr; // The widget that will be used as the label for the check box. + QString check_box_label_property; // The property of the widget that will be used to update the label text. + + if (include_on_off_indicator) + { + // Set the widget and property to use for the check box label. + check_box_label_widget = ui_->driver_overrides_notification_check_box_label_; + check_box_label_property = "text"; + } + else + { + // Delete unnecessary widgets. + + // Remove left_horizontal_spacer_ from the horizontal layout. + int spacer_id = ui_->horizontal_layout_->indexOf(ui_->left_horizontal_spacer_); + Q_ASSERT(spacer_id != -1); + if (spacer_id != -1) + { + ui_->horizontal_layout_->removeItem(ui_->horizontal_layout_->itemAt(spacer_id)); + } + + // Remove the label (in this mode, the check box's label is used instead). + ui_->driver_overrides_notification_check_box_label_->deleteLater(); + + // Set the widget and property to use for the check box label. + check_box_label_widget = ui_->driver_overrides_notification_check_box_; + check_box_label_property = "label_text"; + } + + // Map the model data to the widgets. + result &= driver_overrides_model->BindWidgetToModelAttribute(driver_overrides::kModelAttributeNameNotificationSettingLabel, + check_box_label_widget, + check_box_label_property, + &driver_overrides_checkbox_text_mapper_); + Q_ASSERT(result); + + result &= driver_overrides_model->BindWidgetToModelAttribute(driver_overrides::kModelAttributeNameEnableNotifications, + ui_->driver_overrides_notification_check_box_, + "checked", + &driver_overrides_checkbox_state_mapper_); + Q_ASSERT(result); + + result &= driver_overrides_model->BindWidgetToModelAttribute(driver_overrides::kModelAttributeNameNotificationSettingTitle, + ui_->driver_overrides_settings_title_label_, + "text", + &driver_overrides_widget_text_mapper_); + Q_ASSERT(result); + + // Update the model with the current state of the check box. + result &= driver_overrides_model->SetModelAttributeValue(driver_overrides::kModelAttributeNameEnableNotifications, check_box_state); + Q_ASSERT(result); + + // Set up a connection to handle when the check box is clicked. + connect(ui_->driver_overrides_notification_check_box_, &QCheckBox::clicked, this, &DriverOverridesNotificationConfigWidget::HandleCheckBoxClicked); + + return result; +} + +void DriverOverridesNotificationConfigWidget::HandleCheckBoxClicked(bool checked) +{ + emit StateChanged(checked); +} diff --git a/source/qt_common/custom_widgets/driver_overrides_notification_config_widget.h b/source/qt_common/custom_widgets/driver_overrides_notification_config_widget.h new file mode 100644 index 0000000..6c8dbf3 --- /dev/null +++ b/source/qt_common/custom_widgets/driver_overrides_notification_config_widget.h @@ -0,0 +1,65 @@ +//============================================================================= +// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +/// @author AMD Developer Tools Team +/// @file +/// @brief Header for the custom Driver Overrides notification configuration widget. +/// +/// This DriverOverridesNotificationConfigWidget is a container for a checkbox +/// and a label that is used to enable or disable showing the Driver Overrides +/// notification banner. +//============================================================================= + +#ifndef QTCOMMON_CUSTOM_WIDGETS_DRIVER_OVERRIDES_NOTIFICATION_CONFIG_WIDGET_H_ +#define QTCOMMON_CUSTOM_WIDGETS_DRIVER_OVERRIDES_NOTIFICATION_CONFIG_WIDGET_H_ + +#include +#include + +namespace Ui +{ + class DriverOverridesNotificationConfigWidget; +} + +/// @brief A custom widget with a checkbox setting for the Driver Overrides Notification. +class DriverOverridesNotificationConfigWidget : public QWidget +{ + Q_OBJECT +public: + /// @brief Constructor + /// + /// @param [in] parent The notification settings widget. + explicit DriverOverridesNotificationConfigWidget(QWidget* parent = nullptr); + + /// @brief Destructor. + virtual ~DriverOverridesNotificationConfigWidget(); + + /// @brief Map the Driver Overrides model to the checkbox and label widgets. + /// + /// @param [in] check_box_state The initial state of the checkbox loaded from persistant storage. + /// @param [in] include_on_off_indicator Flag that, if true, causes the on/off indicator to be shown. + /// + /// @return True if the widget was initialized successfully, false otherwise. + bool Init(const bool check_box_state, const bool include_on_off_indicator); + +signals: + /// @brief Signal emitted the checkbox state has changed. + /// + /// @param [in] checked The state of the check box. + void StateChanged(bool checked = false); + +private slots: + /// @brief Slot to handle when the checkBox is clicked. + /// + /// @param [in] checked The state of the check box. + void HandleCheckBoxClicked(bool checked = false); + +protected: + Ui::DriverOverridesNotificationConfigWidget* ui_; ///< The UI object for the widget. + + QDataWidgetMapper driver_overrides_widget_text_mapper_; ///< Data widget mapper for binding model properties Driver Overrides don't show again label. + QDataWidgetMapper + driver_overrides_checkbox_text_mapper_; ///< Data widget mapper for binding model properties to Driver Overrides don't show again checkbox. + QDataWidgetMapper + driver_overrides_checkbox_state_mapper_; ///< Data widget mapper for binding model properties to Driver Overrides don't show again checkbox. +}; +#endif // QTCOMMON_CUSTOM_WIDGETS_DRIVER_OVERRIDES_NOTIFICATION_CONFIG_WIDGET_H_ diff --git a/source/qt_common/custom_widgets/driver_overrides_notification_config_widget.ui b/source/qt_common/custom_widgets/driver_overrides_notification_config_widget.ui new file mode 100644 index 0000000..0e50356 --- /dev/null +++ b/source/qt_common/custom_widgets/driver_overrides_notification_config_widget.ui @@ -0,0 +1,153 @@ + + + DriverOverridesNotificationConfigWidget + + + + 0 + 0 + 1054 + 75 + + + + + 0 + 0 + + + + Form + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + true + + + + [Driver Overrides Tree Title] + + + + + + + 6 + + + 0 + + + + + + 0 + 0 + + + + [Driver Overrides notification config setting] + + + 0 + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Fixed + + + + 20 + 0 + + + + + + + + + 0 + 0 + + + + OFF + + + + + + + Qt::Orientation::Horizontal + + + + 20 + 0 + + + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 20 + 20 + + + + + + + + + ScaledLabel + QLabel +
qt_common/custom_widgets/scaled_label.h
+ 1 +
+ + CheckBoxWidget + QCheckBox +
qt_common/custom_widgets/check_box_widget.h
+
+
+ + + + +
diff --git a/source/qt_common/custom_widgets/driver_overrides_tree_widget.cpp b/source/qt_common/custom_widgets/driver_overrides_tree_widget.cpp new file mode 100644 index 0000000..4d4751f --- /dev/null +++ b/source/qt_common/custom_widgets/driver_overrides_tree_widget.cpp @@ -0,0 +1,137 @@ +//============================================================================= +// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +/// @author AMD Developer Tools Team +/// @file +/// @brief Implementation of the custom tree view widget for driver overrides. +//============================================================================= + +#include "driver_overrides_tree_widget.h" +#include "ui_driver_overrides_tree_widget.h" + +#include "driver_overrides_model.h" +#include "scaled_label.h" +#include "scaled_tree_view.h" + +#include +#include +#include +#include +#include + +DriverOverridesTreeWidget::DriverOverridesTreeWidget(QWidget* parent) + : QWidget(parent) + , ui_(new Ui::DriverOverridesTreeWidget) +{ + ui_->setupUi(this); + Init(); +} + +DriverOverridesTreeWidget::~DriverOverridesTreeWidget() +{ +} + +void DriverOverridesTreeWidget::Init() +{ + // Set up the model for the widgets. + driver_overrides::DriverOverridesModel* driver_overrides_model = driver_overrides::DriverOverridesModel::GetInstance(); + ui_->tree_view_->setModel(driver_overrides_model); + + // Set the root index for the tree view to the root of the driver overrides tree in the model. + // This allows the tree view to display only the driver overrides and not other properties in the model. + ui_->tree_view_->setRootIndex(driver_overrides_model->GetDriverOverridesSubTreeIndex()); + + // Map the model's properties to the widgets. + driver_overrides_model->BindWidgetToModelAttribute( + driver_overrides::kModelAttributeNameTreeViewTitle, ui_->title_label_, "text", &label_title_text_mapper_); + driver_overrides_model->BindWidgetToModelAttribute(driver_overrides::kModelAttributeNameDriverOverridesPresent, this, "visible", &tree_view_mapper_); + + // Override the Scaled Tree View's default settings. + ScaledTreeView* tree_view = ui_->tree_view_; + tree_view->setAlternatingRowColors(false); + tree_view->setSelectionMode(QAbstractItemView::NoSelection); + tree_view->header()->setStretchLastSection(false); + + // If the placeholder widget (i.e. DriverOverridesTreeWidget) has a verical size policy of minimum, this is used to indicate that + // the tree view is contained within a scrollarea and should be allowed to expand to its full height. In this case, the tree view's + // virtual size policy should be set to minimum and the horizontal size policy set to preferred to limit the horizontal size rather + // than allowing it to strched to the full window width. + if (sizePolicy().verticalPolicy() == QSizePolicy::Minimum) + { + tree_view->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + } + + // Add an event filter to the tree view to handle displaying tooltips only when the mouse is over text. + tree_view->installEventFilter(this); + + // Add an event filter to the title label so that font changes can be tracked. + // The font used by the model for the tree view is updated to match the title label's font. + ui_->title_label_->installEventFilter(this); + + // Connect the slot to handle expanding the tree view when driver overrides are imported. + connect(driver_overrides_model, &driver_overrides::DriverOverridesModel::DriverOverridesImported, this, &DriverOverridesTreeWidget::UpdateView); +} + +void DriverOverridesTreeWidget::UpdateView() +{ + // Make sure all child items are expanded in the tree view. + ui_->tree_view_->expandRecursively(ui_->tree_view_->rootIndex()); +} + +// Make the tooltip only display when the mouse is hovering over text for an item and not the entire row. +bool DriverOverridesTreeWidget::eventFilter(QObject* object, QEvent* event) +{ + if (object == ui_->tree_view_) + { + if (event->type() == QEvent::ToolTip) + { + bool tooltip_shown = false; + const QHelpEvent* help_event = static_cast(event); + const QModelIndex index = ui_->tree_view_->indexAt(help_event->pos()); + if (index.isValid()) + { + // Get the text for the item. + const QString item_text = index.data(Qt::DisplayRole).toString(); + if (!item_text.isEmpty()) + { + // Get the length in pixels of the text. + QFontMetrics metrics(ui_->tree_view_->font()); + QRect text_bounding_rect = metrics.boundingRect(item_text); + + // Convert the global position of the mouse to the local position of the tree view item. + const QPoint local_pos = ui_->tree_view_->viewport()->mapFromGlobal(help_event->globalPos()); + + // Adjust the bounding rectangle to the tree view item. + text_bounding_rect.moveTo(ui_->tree_view_->visualRect(index).topLeft()); + + // If the mouse is over the text, show the tooltip. + if (text_bounding_rect.contains(local_pos)) + { + const QString tooltip_text = index.data(driver_overrides::DriverOverridesModel::custom_tooltip_role_).toString(); + QToolTip::showText(help_event->globalPos(), tooltip_text); + tooltip_shown = true; + } + } + } + + if (!tooltip_shown) + { + QToolTip::hideText(); + event->ignore(); + } + } + } + else if (object == ui_->title_label_) + { + if (event->type() == QEvent::FontChange) + { + // Process the event so the label's font is updated. + QWidget::eventFilter(object, event); + + // Make the model's font match the title label's font. + driver_overrides::DriverOverridesModel::GetInstance()->SetDefaultItemFont(ui_->title_label_->font()); + event->ignore(); + } + } + + return QWidget::eventFilter(object, event); +} diff --git a/source/qt_common/custom_widgets/driver_overrides_tree_widget.h b/source/qt_common/custom_widgets/driver_overrides_tree_widget.h new file mode 100644 index 0000000..e36fde9 --- /dev/null +++ b/source/qt_common/custom_widgets/driver_overrides_tree_widget.h @@ -0,0 +1,51 @@ +//============================================================================= +// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +/// @author AMD Developer Tools Team +/// @file +/// @brief Header file for a widget that displays the driver overrides tree. +//============================================================================= + +#ifndef QTCOMMON_CUSTOM_WIDGETS_DRIVER_OVERRIDES_TREE_WIDGET_H_ +#define QTCOMMON_CUSTOM_WIDGETS_DRIVER_OVERRIDES_TREE_WIDGET_H_ + +#include +#include +#include + +namespace Ui +{ + class DriverOverridesTreeWidget; +} + +/// @brief A widget that displays the driver overrides tree. +class DriverOverridesTreeWidget : public QWidget +{ + Q_OBJECT +public: + /// @brief Constructor + /// @param [in] parent The parent of the DriverOverridesTreeWidget + explicit DriverOverridesTreeWidget(QWidget* parent = nullptr); + + /// @brief Destructor. + virtual ~DriverOverridesTreeWidget(); + + /// @brief Create and configure the sub-widgets and layouts for the DriverOverridesTreeWidget. + void Init(); + + /// @brief Event filter to handle displaying the treeview tooltips. + /// @param [in] object The object that the event is for. + /// @param [in] event The event that is being processed. + /// @return True if the event was handled, false otherwise. + bool eventFilter(QObject* object, QEvent* event); + +private slots: + /// @brief Slot to handle when the model has been updated. + void UpdateView(); + +private: + Ui::DriverOverridesTreeWidget* ui_; ///< The Qt ui form. + + QDataWidgetMapper label_title_text_mapper_; ///< The data widget mapper for the title label text. + QDataWidgetMapper tree_view_mapper_; ///< The data widget mapper for the tree view. +}; +#endif // QTCOMMON_CUSTOM_WIDGETS_DRIVER_OVERRIDES_TREE_WIDGET_H_ diff --git a/source/qt_common/custom_widgets/driver_overrides_tree_widget.ui b/source/qt_common/custom_widgets/driver_overrides_tree_widget.ui new file mode 100644 index 0000000..588499f --- /dev/null +++ b/source/qt_common/custom_widgets/driver_overrides_tree_widget.ui @@ -0,0 +1,140 @@ + + + DriverOverridesTreeWidget + + + + 0 + 0 + 1054 + 290 + + + + Form + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 20 + 20 + + + + + + + + + true + + + + [Driver Overrides Tree Title] + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Plain + + + QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents + + + QAbstractItemView::EditTrigger::NoEditTriggers + + + false + + + QAbstractItemView::SelectionMode::NoSelection + + + QAbstractItemView::SelectionBehavior::SelectRows + + + QAbstractItemView::ScrollMode::ScrollPerPixel + + + false + + + false + + + false + + + false + + + 150 + + + false + + + + + + + + ScaledLabel + QLabel +
qt_common/custom_widgets/scaled_label.h
+ 1 +
+ + ScaledTreeView + QTreeView +
qt_common/custom_widgets/scaled_tree_view.h
+
+
+ + + + +
diff --git a/source/qt_common/custom_widgets/navigation_list_widget.cpp b/source/qt_common/custom_widgets/navigation_list_widget.cpp index 2e4ce18..0657a91 100644 --- a/source/qt_common/custom_widgets/navigation_list_widget.cpp +++ b/source/qt_common/custom_widgets/navigation_list_widget.cpp @@ -22,6 +22,8 @@ NavigationListWidget::NavigationListWidget(QWidget* parent) SetStyleSheet(); connect(&ScalingManager::Get(), &ScalingManager::ScaleFactorChanged, this, &NavigationListWidget::OnScaleFactorChanged); + + connect(&QtCommon::QtUtils::ColorTheme::Get(), &QtCommon::QtUtils::ColorTheme::ColorThemeUpdated, this, &NavigationListWidget::SetStyleSheet); } NavigationListWidget::~NavigationListWidget() diff --git a/source/qt_common/custom_widgets/scaled_link_label.cpp b/source/qt_common/custom_widgets/scaled_link_label.cpp new file mode 100644 index 0000000..93d018c --- /dev/null +++ b/source/qt_common/custom_widgets/scaled_link_label.cpp @@ -0,0 +1,103 @@ +//============================================================================= +/// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +/// \author AMD Developer Tools Team +/// \file +/// \brief Implementation for QLabel derived object that contains a hyperlink. +//============================================================================= +#include "scaled_link_label.h" + +#include + +#include "common_definitions.h" +#include "qt_util.h" +#include "scaling_manager.h" + +static const QString kStyledLinkHtml = "%3"; + +ScaledLinkLabel::ScaledLinkLabel(QWidget* parent) + : ScaledLabel(parent) +{ + installEventFilter(this); +} + +ScaledLinkLabel::~ScaledLinkLabel() +{ +} + +const QString& ScaledLinkLabel::GetLinkText() const +{ + return link_text_; +} + +const QString& ScaledLinkLabel::GetLinkUrl() const +{ + return link_url_; +} + +void ScaledLinkLabel::SetLink(const QString& text, const QString& url) +{ + link_text_ = text; + link_url_ = url; + UpdateLinkColor(); +} + +const QColor& ScaledLinkLabel::GetLinkColor() const +{ + return link_color_; +} + +void ScaledLinkLabel::SetLinkColor(const QColor& color) +{ + link_color_ = color; + UpdateLinkColor(); +} + +const QColor& ScaledLinkLabel::GetDisabledLinkColor() const +{ + return disabled_link_color_; +} + +void ScaledLinkLabel::SetDisabledLinkColor(const QColor& color) +{ + disabled_link_color_ = color; + UpdateLinkColor(); +} + +void ScaledLinkLabel::UpdateLinkColor() +{ + QString color; + if (isEnabled()) + { + color = link_color_.name(); + } + else + { + color = disabled_link_color_.name(); + } + setText(kStyledLinkHtml.arg(color, link_url_, link_text_)); +} + +bool ScaledLinkLabel::eventFilter(QObject* object, QEvent* event) +{ + const bool return_value = ScaledLabel::eventFilter(object, event); + + if (object == this) + { + if ((event->type() == QEvent::EnabledChange) || (event->type() == QEvent::PaletteChange) || (event->type() == QEvent::Polish)) + { + UpdateLinkColor(); + + // Set the mouse cursor if the link is enabled. + if (isEnabled()) + { + setCursor(Qt::PointingHandCursor); + } + else + { + setCursor(Qt::ArrowCursor); + } + } + } + + return return_value; +} diff --git a/source/qt_common/custom_widgets/scaled_link_label.h b/source/qt_common/custom_widgets/scaled_link_label.h new file mode 100644 index 0000000..7a2a6ce --- /dev/null +++ b/source/qt_common/custom_widgets/scaled_link_label.h @@ -0,0 +1,85 @@ +//============================================================================= +/// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +/// \author AMD Developer Tools Team +/// \file +/// \brief Definition for QLabel derived object that contains a hyperlink. +//============================================================================= + +#ifndef QTCOMMON_CUSTOM_WIDGETS_SCALED_LINK_LABEL_H_ +#define QTCOMMON_CUSTOM_WIDGETS_SCALED_LINK_LABEL_H_ + +#include "scaled_label.h" + +/// Support for a QLabel that contains a hyperlink. +class ScaledLinkLabel : public ScaledLabel +{ + Q_OBJECT + Q_PROPERTY(QColor linkColor READ GetLinkColor WRITE SetLinkColor) + Q_PROPERTY(QColor disabledLinkColor READ GetDisabledLinkColor WRITE SetDisabledLinkColor) + +public: + /// @brief ScaledLinkLabel constructor. + /// + /// @param [in] parent The parent widget. + explicit ScaledLinkLabel(QWidget* parent = nullptr); + + /// @brief Virtual destructor. + virtual ~ScaledLinkLabel(); + + /// @brief Get the text caption for the link. + /// + /// @return The link text. + const QString& GetLinkText() const; + + /// @brief Get the URL used for link. + /// + /// @return The link URL. + const QString& GetLinkUrl() const; + + /// @brief Set the link text and URL. + /// + /// @param [in] text The text to display. + /// @param [in] url The URL to navigate to. + void SetLink(const QString& text, const QString& url); + + /// @brief Get the text color for the link. + /// + /// @return The link text color. + const QColor& GetLinkColor() const; + + /// @brief Set the text color for the link. + /// + /// @param [in] color The color to set. + void SetLinkColor(const QColor& color); + + /// @brief Get the text color for the link when disabled. + /// + /// @return The disabled link text color. + const QColor& GetDisabledLinkColor() const; + + /// @brief Set the text color for the link when disabled. + /// + /// @param [in] color The color to set. + void SetDisabledLinkColor(const QColor& color); + +protected: + /// @brief Update color for the link the HTML text string. + void UpdateLinkColor(); + + /// @brief Event filter for the link label. + /// + /// Handles updating the link color when a label is enabled/disabled or the color style changes. + /// + /// @param [in] object The object that the event is for. + /// @param [in] event The event that occurred. + /// + /// @return True if the event was handled, false otherwise. + bool eventFilter(QObject* object, QEvent* event); + +private: + QString link_url_; ///< The URL to navigate to. + QString link_text_; ///< The text to display. + QColor link_color_; ///< The color of the link. + QColor disabled_link_color_; ///< The color of the link when disabled. +}; +#endif // QTCOMMON_CUSTOM_WIDGETS_SCALED_LINK_LABEL_H_ diff --git a/source/qt_common/custom_widgets/shared_isa_tree_view.cpp b/source/qt_common/custom_widgets/shared_isa_tree_view.cpp index 6bbe164..7f39ac8 100644 --- a/source/qt_common/custom_widgets/shared_isa_tree_view.cpp +++ b/source/qt_common/custom_widgets/shared_isa_tree_view.cpp @@ -301,7 +301,16 @@ void SharedIsaTreeView::ReplayBranchOrLabelSelection(QModelIndex branch_label_so void SharedIsaTreeView::drawRow(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { - const bool even_row = ((index.data(Qt::ItemDataRole::DisplayRole).toInt()) % 2) == 0; // Assume first column / line number column. + int row_height = option.rect.height(); + if (row_height == 0) + { + // If the row height is zero we wouldn't paint anything anyway. + return; + } + + int y_coordinate_row = option.rect.y() / row_height; + + const bool even_row = (y_coordinate_row % 2) == 0; // Assume first column / line number column. const QColor background_row_color = QtCommon::QtUtils::ColorTheme::Get().GetCurrentThemeColors().isa_background_row_color; const QColor search_match_row_color = QtCommon::QtUtils::ColorTheme::Get().GetCurrentThemeColors().isa_search_match_row_color; @@ -332,23 +341,26 @@ void SharedIsaTreeView::drawRow(QPainter* painter, const QStyleOptionViewItem& o { const QAbstractItemModel* proxy_model = index.model(); - int column_x_pos = -horizontalScrollBar()->value(); - for (int i = 0; i < proxy_model->columnCount(); i++) + if (proxy_model != nullptr) { - QRect index_rect = option.rect; - int column_width = header()->sectionSize(header()->logicalIndex(i)); + int column_x_pos = -horizontalScrollBar()->value(); + for (int i = 0; i < proxy_model->columnCount(); i++) + { + QRect index_rect = option.rect; + int column_width = header()->sectionSize(header()->logicalIndex(i)); - index_rect.setX(column_x_pos); - index_rect.setWidth(column_width); + index_rect.setX(column_x_pos); + index_rect.setWidth(column_width); - painter->save(); - auto pen = painter->pen(); - pen.setColor(QtCommon::QtUtils::ColorTheme::Get().GetCurrentThemeColors().column_separator_color); - painter->setPen(pen); - painter->drawLine(index_rect.topRight(), index_rect.bottomRight()); - painter->restore(); + painter->save(); + auto pen = painter->pen(); + pen.setColor(QtCommon::QtUtils::ColorTheme::Get().GetCurrentThemeColors().column_separator_color); + painter->setPen(pen); + painter->drawLine(index_rect.topRight(), index_rect.bottomRight()); + painter->restore(); - column_x_pos += column_width; + column_x_pos += column_width; + } } } @@ -506,30 +518,28 @@ void SharedIsaTreeView::ScrollBarScrolled(int value) { Q_UNUSED(value); - QVector roles; - roles.push_back(Qt::DisplayRole); - - QAbstractItemModel* model = this->model(); - const QModelIndex top_left = indexAt(QPoint(0, 0)); - const QModelIndex bottom_right = indexAt(QPoint(viewport()->width() - 1, viewport()->height() - 1)); + QAbstractItemModel* model = this->model(); + if (nullptr != model) + { + const QModelIndex top_left = indexAt(QPoint(0, 0)); - const QModelIndex last_pinned_parent_index = model->index(last_pinned_row_.first, 0, QModelIndex()); - const QModelIndex last_pinned_index = model->index(last_pinned_row_.second, 0, last_pinned_parent_index); + const QModelIndex last_pinned_parent_index = model->index(last_pinned_row_.first, 0, QModelIndex()); + const QModelIndex last_pinned_index = model->index(last_pinned_row_.second, 0, last_pinned_parent_index); - if (last_pinned_index.isValid() && last_pinned_index.model() == model) - { - setFirstColumnSpanned(last_pinned_index.row(), last_pinned_index.parent(), false); - } - if (!isFirstColumnSpanned(top_left.row(), top_left.parent())) - { - setFirstColumnSpanned(top_left.row(), top_left.parent(), true); - last_pinned_row_ = std::pair(top_left.parent().row(), top_left.row()); - } - else - { - ClearLastPinnedndex(); + if (last_pinned_index.isValid() && last_pinned_index.model() == model) + { + setFirstColumnSpanned(last_pinned_index.row(), last_pinned_index.parent(), false); + } + if (!isFirstColumnSpanned(top_left.row(), top_left.parent())) + { + setFirstColumnSpanned(top_left.row(), top_left.parent(), true); + last_pinned_row_ = std::pair(top_left.parent().row(), top_left.row()); + } + else + { + ClearLastPinnedndex(); + } } - - // Notify the model/view/controller to refresh all visible rows. - emit model->dataChanged(top_left, bottom_right, roles); + // Notify the viewport to refresh. + viewport()->update(); } diff --git a/source/qt_common/custom_widgets/shared_isa_widget.cpp b/source/qt_common/custom_widgets/shared_isa_widget.cpp index 1d9831e..230cdfb 100644 --- a/source/qt_common/custom_widgets/shared_isa_widget.cpp +++ b/source/qt_common/custom_widgets/shared_isa_widget.cpp @@ -489,7 +489,7 @@ void SharedIsaWidget::ShowHideColumnClicked(bool checked) child->setEnabled(true); } - if (check_count == 1) + if (check_count == 1 && nullptr != last_checked) { last_checked->setEnabled(false); } diff --git a/source/qt_common/utils/common_definitions.h b/source/qt_common/utils/common_definitions.h index 9d61c9a..ddd6fbf 100644 --- a/source/qt_common/utils/common_definitions.h +++ b/source/qt_common/utils/common_definitions.h @@ -121,4 +121,15 @@ enum ColorThemeType kColorThemeTypeCount ///< Number of theme types. }; +namespace QtCommon::QtUtils +{ + /// @brief Defines typesafe color options that map to ColorThemeType + enum class ColorThemeOption : uint8_t + { + kLight = kColorThemeTypeLight, + kDark = kColorThemeTypeDark, + kSystem + }; +} + #endif // QTCOMMON_UTILS_COMMON_DEFINITIONS_H_ diff --git a/source/qt_common/utils/qt_util.cpp b/source/qt_common/utils/qt_util.cpp index 2bf5155..f23af4f 100644 --- a/source/qt_common/utils/qt_util.cpp +++ b/source/qt_common/utils/qt_util.cpp @@ -159,8 +159,8 @@ namespace QtCommon QHeaderView* header = table->horizontalHeader(); if (header != nullptr) { - table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - const int column_count = table->horizontalHeader()->count(); + header->setSectionResizeMode(QHeaderView::ResizeToContents); + const int column_count = header->count(); QVector column_widths(column_count); QAbstractItemModel* model = (QAbstractItemModel*)table->model(); @@ -472,10 +472,6 @@ namespace QtCommon { QMessageBox message_box(parent); -#ifdef Q_OS_WIN - SetDarkWindowTitleBar(message_box.winId(), ColorTheme::Get().GetColorTheme() == kColorThemeTypeDark); -#endif - message_box.setWindowTitle(title); message_box.setText(message); message_box.setStandardButtons(buttons); @@ -485,24 +481,6 @@ namespace QtCommon return message_box.exec(); } -#ifdef Q_OS_WIN - void QtUtils::SetDarkWindowTitleBar(WId window_id, bool set_dark) - { - HWND hwnd = reinterpret_cast(window_id); - - HMODULE user32 = GetModuleHandleW(L"user32.dll"); - fnSetWindowCompositionAttribute SetWindowCompositionAttribute = - reinterpret_cast(GetProcAddress(user32, "SetWindowCompositionAttribute")); - BOOL dark = TRUE; - if (!set_dark) - { - dark = FALSE; - } - WindowCompositioAttributeData attrib_data = {kUseDarkModeColors, &dark, sizeof(dark)}; - SetWindowCompositionAttribute(hwnd, &attrib_data); - } -#endif - ColorThemeType QtUtils::DetectOsSetting() { #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) @@ -613,11 +591,11 @@ namespace QtCommon theme_colors_[kColorThemeTypeDark].graphics_scene_background_color = QColor(20, 20, 20); theme_colors_[kColorThemeTypeDark].link_button_style_sheet = kDarkLinkButtonStylesheet; - QColor white_color = Qt::white; - QColor very_light_color = QColor(240, 240, 240); - QColor black_text = Qt::black; - QColor disabled_color = Qt::gray; - QColor tooltip_color = QColor(240, 230, 200); + const QColor white_color = Qt::white; + const QColor very_light_color = QColor(240, 240, 240); + const QColor black_text = Qt::black; + const QColor disabled_color = Qt::gray; + const QColor tooltip_color = QColor(240, 230, 200); palette_[kColorThemeTypeLight].setColor(QPalette::Window, white_color); palette_[kColorThemeTypeLight].setColor(QPalette::WindowText, black_text); @@ -638,10 +616,17 @@ namespace QtCommon palette_[kColorThemeTypeLight].setColor(QPalette::ToolTipBase, tooltip_color); palette_[kColorThemeTypeLight].setColor(QPalette::ToolTipText, black_text); - QColor very_dark_color = QColor(20, 20, 20); - QColor dark_color = QColor(35, 35, 35); - QColor less_dark_color = QColor(60, 60, 60); - QColor white_text = QColor(240, 240, 240); + // The following should match Qt's default fusion palette for Light Mode on Windows + palette_[kColorThemeTypeLight].setColor(QPalette::Light, QColor(255, 255, 255, 255)); + palette_[kColorThemeTypeLight].setColor(QPalette::Midlight, QColor(202, 202, 202, 255)); + palette_[kColorThemeTypeLight].setColor(QPalette::Mid, QColor(184, 184, 184, 255)); + palette_[kColorThemeTypeLight].setColor(QPalette::Dark, QColor(159, 159, 159, 255)); + + const QColor very_dark_color = QColor(20, 20, 20); + const QColor dark_color = QColor(35, 35, 35); + const QColor less_dark_color = QColor(60, 60, 60); + const QColor white_text = QColor(240, 240, 240); + const QColor highlight_dark = QColor(40, 80, 160, 140); palette_[kColorThemeTypeDark].setColor(QPalette::Window, dark_color); palette_[kColorThemeTypeDark].setColor(QPalette::WindowText, white_text); @@ -655,13 +640,19 @@ namespace QtCommon palette_[kColorThemeTypeDark].setColor(QPalette::Disabled, QPalette::ButtonText, disabled_color); palette_[kColorThemeTypeDark].setColor(QPalette::BrightText, Qt::red); palette_[kColorThemeTypeDark].setColor(QPalette::Link, QColor(42, 130, 218)); - palette_[kColorThemeTypeDark].setColor(QPalette::Highlight, QColor(40, 80, 160, 140)); + palette_[kColorThemeTypeDark].setColor(QPalette::Highlight, highlight_dark); palette_[kColorThemeTypeDark].setColor(QPalette::HighlightedText, Qt::white); palette_[kColorThemeTypeDark].setColor(QPalette::Disabled, QPalette::HighlightedText, disabled_color); palette_[kColorThemeTypeDark].setColor(QPalette::Disabled, QPalette::WindowText, disabled_color); palette_[kColorThemeTypeDark].setColor(QPalette::ToolTipBase, less_dark_color); palette_[kColorThemeTypeDark].setColor(QPalette::ToolTipText, white_text); + // The following should match Qt's default fusion palette values for Dark Mode on Windows + palette_[kColorThemeTypeDark].setColor(QPalette::Light, QColor(75, 75, 75, 255)); + palette_[kColorThemeTypeDark].setColor(QPalette::Midlight, QColor(42, 42, 42, 255)); + palette_[kColorThemeTypeDark].setColor(QPalette::Mid, QColor(38, 38, 38, 255)); + palette_[kColorThemeTypeDark].setColor(QPalette::Dark, QColor(33, 33, 33, 255)); + theme_type_ = kColorThemeTypeLight; } diff --git a/source/qt_common/utils/qt_util.h b/source/qt_common/utils/qt_util.h index 31f3518..c091509 100644 --- a/source/qt_common/utils/qt_util.h +++ b/source/qt_common/utils/qt_util.h @@ -121,13 +121,6 @@ namespace QtCommon /// \return message box exec result int ShowMessageBox(QWidget* parent, QMessageBox::StandardButtons buttons, QMessageBox::Icon icon, const QString& title, const QString& message); -#ifdef Q_OS_WIN - /// \brief Helper function to set the title bar to be dark in windows - /// \param window_id the window id - /// \param set_dark whether the window title bar should be set to dark. - void SetDarkWindowTitleBar(WId window_id, bool set_dark = true); -#endif - /// \brief Detects the app color theme settings of the current OS /// \return The app color theme of the OS. Returns light theme by default. ColorThemeType DetectOsSetting();