diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:51:45 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:51:45 +0300 |
| commit | d3261e64152397db2dca4d691a990c6bc2a6f4dd (patch) | |
| tree | fac2f7be638651181a72453d714f0f96675c2b8b /archived/projt-launcher/launcher/ui/widgets/WideBar.cpp | |
| parent | 31b9a8949ed0a288143e23bf739f2eb64fdc63be (diff) | |
| download | Project-Tick-d3261e64152397db2dca4d691a990c6bc2a6f4dd.tar.gz Project-Tick-d3261e64152397db2dca4d691a990c6bc2a6f4dd.zip | |
NOISSUE add archived projects
Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
Diffstat (limited to 'archived/projt-launcher/launcher/ui/widgets/WideBar.cpp')
| -rw-r--r-- | archived/projt-launcher/launcher/ui/widgets/WideBar.cpp | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/archived/projt-launcher/launcher/ui/widgets/WideBar.cpp b/archived/projt-launcher/launcher/ui/widgets/WideBar.cpp new file mode 100644 index 0000000000..19fc28655e --- /dev/null +++ b/archived/projt-launcher/launcher/ui/widgets/WideBar.cpp @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "WideBar.h" + +#include <QContextMenuEvent> +#include <QCryptographicHash> +#include <QToolButton> + +class ActionButton : public QToolButton +{ + Q_OBJECT + public: + ActionButton(QAction* action, QWidget* parent = nullptr, bool use_default_action = false) + : QToolButton(parent), + m_action(action), + m_use_default_action(use_default_action) + { + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + // workaround for breeze and breeze forks + setProperty("_kde_toolButton_alignment", Qt::AlignLeft); + + if (m_use_default_action) + { + setDefaultAction(action); + } + else + { + connect(this, &ActionButton::clicked, action, &QAction::trigger); + } + connect(action, &QAction::changed, this, &ActionButton::actionChanged); + + actionChanged(); + }; + public slots: + void actionChanged() + { + setEnabled(m_action->isEnabled()); + // better pop up mode + if (m_action->menu()) + { + setPopupMode(QToolButton::MenuButtonPopup); + } + if (!m_use_default_action) + { + setChecked(m_action->isChecked()); + setCheckable(m_action->isCheckable()); + setText(m_action->text()); + setIcon(m_action->icon()); + setToolTip(m_action->toolTip()); + setHidden(!m_action->isVisible()); + } + setFocusPolicy(Qt::NoFocus); + } + + private: + QAction* m_action; + bool m_use_default_action; +}; + +WideBar::WideBar(const QString& title, QWidget* parent) : QToolBar(title, parent) +{ + setFloatable(false); + setMovable(false); + + setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu); + connect(this, &QToolBar::customContextMenuRequested, this, &WideBar::showVisibilityMenu); +} + +WideBar::WideBar(QWidget* parent) : QToolBar(parent) +{ + setFloatable(false); + setMovable(false); + + setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu); + connect(this, &QToolBar::customContextMenuRequested, this, &WideBar::showVisibilityMenu); +} + +void WideBar::addAction(QAction* action) +{ + BarEntry entry; + entry.bar_action = addWidget(new ActionButton(action, this, m_use_default_action)); + entry.menu_action = action; + entry.type = BarEntry::Type::Action; + + m_entries.push_back(entry); + + m_menu_state = MenuState::Dirty; +} + +void WideBar::addSeparator() +{ + BarEntry entry; + entry.bar_action = QToolBar::addSeparator(); + entry.type = BarEntry::Type::Separator; + + m_entries.push_back(entry); +} + +auto WideBar::getMatching(QAction* act) -> QList<BarEntry>::iterator +{ + auto iter = std::find_if(m_entries.begin(), + m_entries.end(), + [act](BarEntry const& entry) { return entry.menu_action == act; }); + + return iter; +} + +void WideBar::insertActionBefore(QAction* before, QAction* action) +{ + auto iter = getMatching(before); + if (iter == m_entries.end()) + return; + + BarEntry entry; + entry.bar_action = insertWidget(iter->bar_action, new ActionButton(action, this, m_use_default_action)); + entry.menu_action = action; + entry.type = BarEntry::Type::Action; + + m_entries.insert(iter, entry); + + m_menu_state = MenuState::Dirty; +} + +void WideBar::insertActionAfter(QAction* after, QAction* action) +{ + auto iter = getMatching(after); + if (iter == m_entries.end()) + return; + + iter++; + // the action to insert after is present + // however, the element after it isn't valid + if (iter == m_entries.end()) + { + // append the action instead of inserting it + addAction(action); + return; + } + + BarEntry entry; + entry.bar_action = insertWidget(iter->bar_action, new ActionButton(action, this, m_use_default_action)); + entry.menu_action = action; + entry.type = BarEntry::Type::Action; + + m_entries.insert(iter, entry); + + m_menu_state = MenuState::Dirty; +} + +void WideBar::insertWidgetBefore(QAction* before, QWidget* widget) +{ + auto iter = getMatching(before); + if (iter == m_entries.end()) + return; + + insertWidget(iter->bar_action, widget); +} + +void WideBar::insertSpacer(QAction* action) +{ + auto iter = getMatching(action); + if (iter == m_entries.end()) + return; + + auto* spacer = new QWidget(); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + BarEntry entry; + entry.bar_action = insertWidget(iter->bar_action, spacer); + entry.type = BarEntry::Type::Spacer; + m_entries.insert(iter, entry); +} + +void WideBar::insertSeparator(QAction* before) +{ + auto iter = getMatching(before); + if (iter == m_entries.end()) + return; + + BarEntry entry; + entry.bar_action = QToolBar::insertSeparator(iter->bar_action); + entry.type = BarEntry::Type::Separator; + + m_entries.insert(iter, entry); +} + +QMenu* WideBar::createContextMenu(QWidget* parent, const QString& title) +{ + auto* contextMenu = new QMenu(title, parent); + for (auto& item : m_entries) + { + switch (item.type) + { + default: + case BarEntry::Type::None: break; + case BarEntry::Type::Separator: + case BarEntry::Type::Spacer: contextMenu->addSeparator(); break; + case BarEntry::Type::Action: contextMenu->addAction(item.menu_action); break; + } + } + return contextMenu; +} + +static void copyAction(QAction* from, QAction* to) +{ + Q_ASSERT(from); + Q_ASSERT(to); + + to->setText(from->text()); + to->setIcon(from->icon()); + to->setToolTip(from->toolTip()); +} + +void WideBar::showVisibilityMenu(QPoint const& position) +{ + if (!m_bar_menu) + { + m_bar_menu = std::make_unique<QMenu>(this); + m_bar_menu->setTearOffEnabled(true); + } + + if (m_menu_state == MenuState::Dirty) + { + for (auto* old_action : m_bar_menu->actions()) + old_action->deleteLater(); + + m_bar_menu->clear(); + + m_bar_menu->addActions(m_context_menu_actions); + + m_bar_menu->addSeparator()->setText(tr("Customize toolbar actions")); + + for (auto& entry : m_entries) + { + if (entry.type != BarEntry::Type::Action) + continue; + + auto act = new QAction(); + copyAction(entry.menu_action, act); + + act->setCheckable(true); + act->setChecked(entry.bar_action->isVisible()); + + connect(act, + &QAction::toggled, + entry.bar_action, + [this, &entry](bool toggled) + { + entry.bar_action->setVisible(toggled); + + // NOTE: This is needed so that disabled actions get reflected on the button when it is made + // visible. + static_cast<ActionButton*>(widgetForAction(entry.bar_action))->actionChanged(); + }); + + m_bar_menu->addAction(act); + } + + m_menu_state = MenuState::Fresh; + } + + m_bar_menu->popup(mapToGlobal(position)); +} + +void WideBar::addContextMenuAction(QAction* action) +{ + m_context_menu_actions.append(action); +} + +QByteArray WideBar::getVisibilityState() const +{ + QByteArray state; + + for (auto const& entry : m_entries) + { + if (entry.type != BarEntry::Type::Action) + continue; + + state.append(entry.bar_action->isVisible() ? '1' : '0'); + } + + state.append(','); + state.append(getHash()); + + return state; +} + +void WideBar::setVisibilityState(QByteArray&& state) +{ + auto split = state.split(','); + + auto bits = split.first(); + auto hash = split.last(); + + // If the actions changed, we better not try to load the old one to avoid unwanted hiding + if (!checkHash(hash)) + return; + + qsizetype i = 0; + for (auto& entry : m_entries) + { + if (entry.type != BarEntry::Type::Action) + continue; + if (i == bits.size()) + break; + + entry.bar_action->setVisible(bits.at(i++) == '1'); + + // NOTE: This is needed so that disabled actions get reflected on the button when it is made visible. + static_cast<ActionButton*>(widgetForAction(entry.bar_action))->actionChanged(); + } +} + +QByteArray WideBar::getHash() const +{ + QCryptographicHash hash(QCryptographicHash::Sha1); + for (auto const& entry : m_entries) + { + if (entry.type != BarEntry::Type::Action) + continue; + hash.addData(entry.menu_action->text().toLatin1()); + } + + return hash.result().toBase64(); +} + +bool WideBar::checkHash(QByteArray const& old_hash) const +{ + return old_hash == getHash(); +} + +void WideBar::removeAction(QAction* action) +{ + auto iter = getMatching(action); + if (iter == m_entries.end()) + return; + + iter->bar_action->setVisible(false); + removeAction(iter->bar_action); + m_entries.erase(iter); +} + +#include "WideBar.moc" |
