mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
2025 lines
69 KiB
C++
2025 lines
69 KiB
C++
/**
|
|
* RibbonMainWindow.cpp
|
|
* Copyright (c) 2013 ROBLOX Corp. All rights reserved.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "RobloxRibbonMainWindow.h"
|
|
|
|
// Qt includes
|
|
#include <QCheckBox>
|
|
#include <QComboBox>
|
|
#include <QDesktopServices>
|
|
#include <QDomDocument>
|
|
#include <QDomElement>
|
|
#include <QDir>
|
|
#include <QRadioButton>
|
|
#include <QDialogButtonBox>
|
|
#include <QtCore>
|
|
#include <QWidgetAction>
|
|
#include <QFontDatabase>
|
|
|
|
// Roblox Studio includes
|
|
#include "RobloxSettings.h"
|
|
#include "RobloxMainWindow.h"
|
|
#include "RobloxDocManager.h"
|
|
#include "RobloxIDEDoc.h"
|
|
#include "RobloxCustomWidgets.h"
|
|
#include "GalleryItemColor.h"
|
|
#include "NameValueStoreManager.h"
|
|
#include "CommonInsertWidget.h"
|
|
#include "UpdateUIManager.h"
|
|
#include "AuthoringSettings.h"
|
|
#include "RobloxToolBox.h"
|
|
#include "QtUtilities.h"
|
|
#include "RobloxUser.h"
|
|
#include "PluginAction.h"
|
|
#include "RobloxCustomWidgets.h"
|
|
#include "RobloxQuickAccessConfig.h"
|
|
#include "StudioDeviceEmulator.h"
|
|
|
|
FASTFLAGVARIABLE(SurfaceLightEnabled, false)
|
|
|
|
FASTFLAG(StudioSeparateActionByActivationMethod)
|
|
|
|
//static const char* newLayoutXmlConfigFile = "RobloxStudioRibbonRelease.xml";
|
|
static const char* updatedLayoutXmlConfigFile = "RobloxStudioRibbon.xml";
|
|
|
|
static const char* elemMainWindow = "MainWindow";
|
|
|
|
static const char* elemQuickAccessBar = "QuickAccessBar";
|
|
static const char* elemeSystemButton = "SystemButton";
|
|
static const char* elemSystemPopup = "SystemPopup";
|
|
static const char* elemRibbonBar = "RibbonBar";
|
|
|
|
static const char* elemTabPage = "tabpage";
|
|
static const char* elemGroup = "group";
|
|
|
|
static const char* elemAddAction = "addaction";
|
|
static const char* elemAddRadioButton = "addradiobutton";
|
|
static const char* elemAddCheckBox = "addcheckbox";
|
|
static const char* elemAddComboBox = "addcombobox";
|
|
static const char* elemAddSeparator = "addseparator";
|
|
static const char* elemActionGroup = "actiongroup";
|
|
static const char* elemSubMenu = "submenu";
|
|
static const char* elemMenuButton = "menubutton";
|
|
static const char* elemMenuPopupButton = "menupopupbutton";
|
|
static const char* elemColorPicker = "colorpicker";
|
|
static const char* elemButtonGroup = "buttongroup";
|
|
static const char* elemSplitButton = "splitbutton";
|
|
static const char* elemRibbonGallery = "ribbongallery";
|
|
static const char* elemGalleryItem = "galleryitem";
|
|
static const char* elemComboBoxItem = "comboboxitem";
|
|
static const char* elemGroupOptionsMenu= "groupoptionsmenu";
|
|
|
|
static const char* attribName = "name";
|
|
static const char* attribText = "text";
|
|
static const char* attribStyle = "style";
|
|
static const char* attribPopupStyle = "popupStyle";
|
|
static const char* attribToolTip = "tooltip";
|
|
|
|
static const char* attribIcon = "icon";
|
|
static const char* attribIconWidth = "width";
|
|
static const char* attribIconHeight = "height";
|
|
static const char* attribColumns = "columns";
|
|
|
|
static const char* attribVisible = "visible";
|
|
static const char* attribCheckable = "ischeckable";
|
|
static const char* attribProxyAction = "isProxy";
|
|
static const char* attribEnabled = "enabled";
|
|
static const char* attribRequiresAuth = "requiresauthentication";
|
|
static const char* attribChecked = "checked";
|
|
static const char* attribDefaultValue = "defaultvalue";
|
|
static const char* attribIconVisibleInMenu = "iconvisibleinmenu";
|
|
|
|
static const char* attribType = "type";
|
|
static const char* attribValue = "value";
|
|
static const char* attribInstanceName = "instancename";
|
|
static const char* attribMenuTrigger = "handleMenuTrigger";
|
|
static const char* attribCommonSlot = "iscommonslot";
|
|
static const char* attribPopup = "popup";
|
|
static const char* attribSelectChangesText = "selectChangesText";
|
|
|
|
static const char* attribFFlag = "fflag";
|
|
static const char* attribWinOnly = "iswinonly";
|
|
#ifdef _WIN32
|
|
static const char* attribMacOnly = "ismaconly";
|
|
#endif
|
|
|
|
static const char* attribMinButton = "minimizeButton";
|
|
static const char* attribThemeOptions = "themeoptions";
|
|
static const char* attribToolTipIcon = "tooltipicon";
|
|
static const char* attribFont = "font";
|
|
|
|
static const char* attribContextTitle = "contexttitle";
|
|
static const char* attribContextColor = "contextcolor";
|
|
|
|
static const char* attribItemWidth = "itemwidth";
|
|
static const char* attribItemHeight = "itemheight";
|
|
static const char* attribSetting = "setting";
|
|
|
|
static const char* actionCheckBox = "checkbox";
|
|
static const char* actionRadio = "radio";
|
|
static const char* actionPlugin = "plugin";
|
|
|
|
static const char* settingRibbonMinimized = "rbxRibbonMinimized";
|
|
static const char* settingRecentFiles = "rbxRecentFiles";
|
|
static const char* settingLastDirectory = "rbxl_last_directory";
|
|
|
|
RobloxRibbonMainWindow::RobloxRibbonMainWindow(RobloxMainWindow* pMainWindow)
|
|
: Qtitan::RibbonMainWindow(NULL)
|
|
, m_LastDirectory(".")
|
|
, m_pRibbonStyle(NULL)
|
|
, m_pRibbonMinimizeAction(NULL)
|
|
, m_pQuickAccessConfigDialog(NULL)
|
|
, m_bInitialized(false)
|
|
{
|
|
// DO NOT ADD MORE HERE
|
|
// Try to keep most initialization logic in setupRibbonBar
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::setupRibbonBar()
|
|
{
|
|
if (m_bInitialized)
|
|
throw RBX::runtime_error("Ribbon bar already initialized.");
|
|
|
|
//create ribbonbar
|
|
QString xmlFilePath(RobloxSettings::getResourcesFolder());
|
|
xmlFilePath.append("/");
|
|
xmlFilePath.append(updatedLayoutXmlConfigFile);
|
|
|
|
QFile file(xmlFilePath);
|
|
if (!file.open(QIODevice::ReadOnly))
|
|
throw RBX::runtime_error("Failed to open configuration XML file.");
|
|
|
|
QDomDocument doc("appRibbonBar");
|
|
if (!doc.setContent(&file))
|
|
throw RBX::runtime_error("Failed to parse configuration XML file.");
|
|
|
|
QDomElement docElem = doc.documentElement();
|
|
if(docElem.tagName() != elemMainWindow)
|
|
throw RBX::runtime_error("Failed to parse configuration XML file. File must start with MainWindow element!");
|
|
|
|
// initialize default data and do required cleanup
|
|
initialize();
|
|
|
|
// now create system button
|
|
parseAndCreateSystemButton(docElem);
|
|
|
|
// finally ribbon bar
|
|
parseAndCreateRibbonBar(docElem);
|
|
|
|
// quickaccess bar must be created in the last, as there can be some actions created from XML file
|
|
parseAndCreateQuickAccessBar(docElem);
|
|
|
|
ribbonBar()->setFrameThemeEnabled(false);
|
|
ribbonBar()->setTitleBarVisible(false);
|
|
ribbonBar()->getSystemButton()->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
|
|
|
m_bInitialized = true;
|
|
}
|
|
|
|
RibbonPage* RobloxRibbonMainWindow::createRibbonPage(const QString& objectName, const QString& pageTitle, int indexPosition)
|
|
{
|
|
RibbonPage* pRibbonPage = ribbonBar()->findChild<RibbonPage*>(objectName);
|
|
if (pRibbonPage)
|
|
return pRibbonPage;
|
|
|
|
// Pages should be tacked onto the end of the bar, unless an explicit index has been passed in.
|
|
int insertedPosition = indexPosition >= 0 ? indexPosition : ribbonBar()->getPageCount();
|
|
|
|
pRibbonPage = ribbonBar()->insertPage(insertedPosition, pageTitle);
|
|
RBXASSERT(pRibbonPage);
|
|
pRibbonPage->setObjectName(objectName);
|
|
|
|
// update the index property of this page and all pages after
|
|
for (int ii = insertedPosition; ii < ribbonBar()->getPageCount(); ii++)
|
|
ribbonBar()->getPage(ii)->setProperty("index", ii);
|
|
|
|
return pRibbonPage;
|
|
}
|
|
|
|
RibbonGroup* RobloxRibbonMainWindow::createRibbonGroup(RibbonPage* pParent, const QString& objectName, const QString& groupTitle)
|
|
{
|
|
if (!pParent)
|
|
return NULL;
|
|
|
|
RibbonGroup* pRibbonGroup = pParent->findChild<RibbonGroup*>(objectName);
|
|
if (pRibbonGroup)
|
|
return pRibbonGroup;
|
|
|
|
pRibbonGroup = pParent->addGroup(groupTitle.isEmpty() ? objectName : groupTitle);
|
|
RBXASSERT(pRibbonGroup);
|
|
pRibbonGroup->setObjectName(objectName);
|
|
|
|
return pRibbonGroup;
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::parseAndCreateQuickAccessBar(const QDomElement &docElem)
|
|
{
|
|
QDomElement quickAccessBarElement = docElem.firstChildElement(elemQuickAccessBar);
|
|
if (quickAccessBarElement.isNull())
|
|
return;
|
|
|
|
Qtitan::RibbonQuickAccessBar* pQuickAccessBar = ribbonBar()->getQuickAccessBar();
|
|
if (!pQuickAccessBar)
|
|
return;
|
|
|
|
QString toolTip = getToolTip(quickAccessBarElement);
|
|
if (!toolTip.isEmpty())
|
|
{
|
|
QAction* pAction = pQuickAccessBar->actionCustomizeButton();
|
|
if (pAction)
|
|
pAction->setToolTip(tr("Customize Quick Access Bar"));
|
|
}
|
|
|
|
QString visible = quickAccessBarElement.attribute(attribVisible);
|
|
ribbonBar()->showQuickAccess(visible.isEmpty() || (visible != "false"));
|
|
|
|
// load QuickAccess configuration from settings
|
|
bool configLoaded = RobloxQuickAccessConfig::singleton().loadQuickAccessConfig();
|
|
|
|
RobloxSettings settings;
|
|
// parse data from XML
|
|
RobloxQuickAccessConfig::QuickAccessActionCollection defaultQuickAccessCollection;
|
|
QDomElement addActionElement = quickAccessBarElement.firstChildElement(elemAddAction);
|
|
while (!addActionElement.isNull())
|
|
{
|
|
QString actionName = getName(addActionElement);
|
|
if (!actionName.isEmpty())
|
|
{
|
|
QAction *pChildAction = findChildAction(actionName);
|
|
if (pChildAction)
|
|
{
|
|
// check if action is visible in quick access bar
|
|
bool actVisible = false;
|
|
if (!addActionElement.attribute(attribVisible).isEmpty())
|
|
actVisible = addActionElement.attribute(attribVisible) == "true" ;
|
|
|
|
// if not loaded from settings, then add actions from XML config
|
|
if (!configLoaded)
|
|
{
|
|
QString key("QuickAccess_" + pChildAction->text());
|
|
if (settings.contains(key))
|
|
actVisible = settings.value(key).toBool(); // legacy support
|
|
RobloxQuickAccessConfig::singleton().addToQuickAccessBar(pChildAction, actVisible);
|
|
}
|
|
|
|
// make a list of actions defined in XML config for restore
|
|
defaultQuickAccessCollection.append(RobloxQuickAccessConfig::ActionData(pChildAction, actVisible, true));
|
|
}
|
|
}
|
|
|
|
addActionElement = addActionElement.nextSiblingElement(elemAddAction);
|
|
}
|
|
|
|
// save XML config for restore
|
|
RobloxQuickAccessConfig::singleton().storeDefaults(defaultQuickAccessCollection);
|
|
connect(customizeQuickAccessAction, SIGNAL(triggered()), this, SLOT(customizeQuickAccess()));
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::updateInternalWidgetsState(QAction* pAction, bool enabledState,bool checkedState)
|
|
{
|
|
// update internal widgets is required only if we have ribbon bar
|
|
if (QAbstractButton* pCorrelaryButton = qobject_cast<QAbstractButton*>(m_mCorrelaryActionMap.value(pAction)))
|
|
{
|
|
if (QThread::currentThread() == thread())
|
|
{
|
|
pCorrelaryButton->setEnabled(enabledState);
|
|
pCorrelaryButton->setChecked(checkedState);
|
|
}
|
|
else
|
|
{
|
|
QMetaObject::invokeMethod(pCorrelaryButton,"setEnabled",Qt::QueuedConnection,Q_ARG(bool,enabledState));
|
|
QMetaObject::invokeMethod(pCorrelaryButton,"setChecked",Qt::QueuedConnection,Q_ARG(bool,checkedState));
|
|
}
|
|
}
|
|
}
|
|
|
|
QString RobloxRibbonMainWindow::getDefaultSavePath()
|
|
{
|
|
QString doc_path = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
|
|
QDir(doc_path).mkdir("ROBLOX");
|
|
return doc_path + "/ROBLOX";
|
|
}
|
|
|
|
QStringList RobloxRibbonMainWindow::getRecentFiles() const
|
|
{ return RobloxSettings().value(settingRecentFiles).toStringList(); }
|
|
|
|
/**
|
|
* Sets the current path and stores it in the settings.
|
|
* If the path is null, the default path will be used.
|
|
* If the path is a filename, the filename part will be stripped off.
|
|
*
|
|
* @param path new path for current directory
|
|
*/
|
|
void RobloxRibbonMainWindow::setCurrentDirectory(const QString& path)
|
|
{
|
|
QString newPath = path;
|
|
QFileInfo info(newPath);
|
|
|
|
// if it's a file, get the path
|
|
if (info.isFile())
|
|
newPath = info.absolutePath();
|
|
else
|
|
newPath = info.absoluteFilePath();
|
|
|
|
// if empty or doesn't exist, get the default path
|
|
if ( newPath.isEmpty() || !QDir(newPath).exists() )
|
|
newPath = getDefaultSavePath();
|
|
|
|
// don't change to the auto-save path
|
|
if ( newPath == AuthoringSettings::singleton().autoSaveDir.absolutePath() )
|
|
return;
|
|
|
|
m_LastDirectory = newPath;
|
|
QDir::setCurrent(m_LastDirectory);
|
|
|
|
// save into settings
|
|
RobloxSettings().setValue(settingLastDirectory, m_LastDirectory);
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::updateRecentFile(const QString& fileName)
|
|
{
|
|
//update the recent files
|
|
if(!fileName.isEmpty()
|
|
&& (fileName.endsWith(".rbxl", Qt::CaseInsensitive) || fileName.endsWith(".rbxlx", Qt::CaseInsensitive))
|
|
&& !(fileName.endsWith("visit.rbxl", Qt::CaseSensitive))
|
|
&& !(fileName.endsWith("server.rbxl", Qt::CaseSensitive)))
|
|
{
|
|
RobloxSettings settings;
|
|
QStringList files = getRecentFiles();
|
|
files.removeAll(fileName);
|
|
|
|
QFileInfo fileInfo(fileName);
|
|
bool bFileExists = fileInfo.exists();
|
|
if (bFileExists && !files.contains(fileName))
|
|
files.prepend(fileName);
|
|
|
|
// if it exceeds max limit then remove last
|
|
while (files.size() > MAX_RECENT_FILES)
|
|
files.removeLast();
|
|
|
|
// update settings
|
|
settings.setValue(settingRecentFiles, files);
|
|
|
|
updateRecentFilesUI();
|
|
|
|
if (bFileExists)
|
|
setCurrentDirectory(fileName);
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::actionEvent(QActionEvent* evt)
|
|
{
|
|
if (!m_bInitialized || m_mProxyActions.isEmpty() || evt->type() != QEvent::ActionAdded)
|
|
return;
|
|
|
|
QMap<QString, QAction*>::iterator iter = m_mProxyActions.find(getFullName(evt->action()));
|
|
if (iter == m_mProxyActions.end())
|
|
return;
|
|
|
|
iter.value()->setCheckable(evt->action()->isCheckable());
|
|
connect(iter.value(), SIGNAL(triggered()), evt->action(), SLOT(trigger()));
|
|
connect(evt->action(), SIGNAL(changed()), this, SLOT(updateProxyAction()));
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::parseAndCreateSystemButton(const QDomElement &docElem)
|
|
{
|
|
QDomElement systemButtonElement = docElem.firstChildElement(elemeSystemButton);
|
|
if (systemButtonElement.isNull())
|
|
return;
|
|
|
|
QString buttonName = getName(systemButtonElement);
|
|
QString icon = systemButtonElement.attribute(attribIcon);
|
|
QString helpIcon = systemButtonElement.attribute(attribToolTipIcon);
|
|
|
|
QIcon iconLogo;
|
|
if (!helpIcon.isEmpty())
|
|
{
|
|
// if sizes of the pixmaps added in QIcon are different then the first added pix will be used in tooltip associated with system button
|
|
// in case of similar sizes second pixmap is used, so make sure we have size for tooltip icon different from 32x32
|
|
QPixmap helpPixmap(helpIcon);
|
|
if (!helpPixmap.isNull())
|
|
iconLogo.addPixmap(helpPixmap);
|
|
}
|
|
|
|
if (!icon.isEmpty())
|
|
{
|
|
QPixmap iconPixmap(icon);
|
|
if (!iconPixmap.isNull())
|
|
iconLogo.addPixmap(iconPixmap.scaled(32, 32, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
|
}
|
|
|
|
if(QAction* actionFile = ribbonBar()->addSystemButton(iconLogo, buttonName))
|
|
{
|
|
// set current theme (default theme)
|
|
setTheme(m_pRibbonStyle->getTheme());
|
|
|
|
QString tooltip = getToolTip(systemButtonElement);
|
|
actionFile->setToolTip(tooltip);
|
|
|
|
QDomElement popupElement = systemButtonElement.firstChildElement(elemSystemPopup);
|
|
if (!popupElement.isNull())
|
|
{
|
|
Qtitan::RibbonSystemPopupBar* popupBar = qobject_cast<Qtitan::RibbonSystemPopupBar*>(actionFile->menu());
|
|
connect(popupBar, SIGNAL(aboutToShow()), &UpdateUIManager::Instance(), SLOT(onMenuShow()));
|
|
|
|
parseAndCreateChildren(popupElement, popupBar);
|
|
|
|
RibbonPageSystemRecentFileList* pageRecentFile = popupBar->addPageRecentFile(tr("Recent Files"));
|
|
pageRecentFile->setSize(MAX_RECENT_FILES);
|
|
pageRecentFile->setObjectName("recentFileList");
|
|
connect(pageRecentFile, SIGNAL(openRecentFile(const QString&)), this, SLOT(openRecentFile(const QString&)));
|
|
|
|
QAction* pActionExit = popupBar->addPopupBarAction(tr("Exit"));
|
|
connect(pActionExit, SIGNAL(triggered()), this, SLOT(close()));
|
|
|
|
//QAction* pActionOption = popupBar->addPopupBarAction(tr("Settings"));
|
|
//connect(pActionOption, SIGNAL(triggered()), this, SLOT(openSettingsDialog()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::parseAndCreateRibbonBar(const QDomElement &docElem)
|
|
{
|
|
QDomElement ribbonBarElement = docElem.firstChildElement(elemRibbonBar);
|
|
if (!ribbonBarElement.isNull())
|
|
{
|
|
// Add online help button
|
|
ribbonBar()->addAction(onlineHelpAction, Qt::ToolButtonIconOnly);
|
|
onlineHelpAction->setIcon(QIcon(":/RibbonBar/images/Studio Ribbon Icons/about.png"));
|
|
|
|
// Add theme style options
|
|
if (ribbonBarElement.attribute(attribThemeOptions) == "true")
|
|
createThemeOptions();
|
|
|
|
// Add a minimize button
|
|
if (ribbonBarElement.attribute(attribMinButton) == "true")
|
|
{
|
|
m_pRibbonMinimizeAction = ribbonBar()->addAction(QIcon(":/RibbonBar/images/Studio Ribbon Icons/ribbonMinimize.png"), "Toggle ribbon visiblity", Qt::ToolButtonIconOnly);
|
|
m_pRibbonMinimizeAction->setStatusTip(tr("Show only the tab names on the Ribbon"));
|
|
m_pRibbonMinimizeAction->setShortcut(tr("Ctrl+F1"));
|
|
if (FFlag::StudioSeparateActionByActivationMethod)
|
|
m_pRibbonMinimizeAction->setObjectName("minimizeRibbonAction");
|
|
QtUtilities::setActionShortcuts(*m_pRibbonMinimizeAction, m_pRibbonMinimizeAction->shortcuts());
|
|
|
|
if (FFlag::StudioSeparateActionByActivationMethod)
|
|
connect(m_pRibbonMinimizeAction, SIGNAL(triggered()), this, SLOT(commonSlot()));
|
|
else
|
|
connect(m_pRibbonMinimizeAction, SIGNAL(triggered()), this, SLOT(toggleRibbonMinimize()));
|
|
connect(ribbonBar(), SIGNAL(minimizationChanged(bool)), this, SLOT(handleRibbonMinimizationChanged(bool)));
|
|
|
|
// add this to registry
|
|
RobloxSettings settings;
|
|
ribbonBar()->setMinimized(settings.value(settingRibbonMinimized, false).toBool());
|
|
}
|
|
|
|
// Create our tab pages and their children, recursively
|
|
parseAndCreateChildren(ribbonBarElement, ribbonBar());
|
|
|
|
if (ribbonBarElement.hasAttribute(attribFont))
|
|
{
|
|
QString fontStr = ribbonBarElement.attribute(attribFont);
|
|
if (fontStr.isEmpty())
|
|
{
|
|
updateFonts(m_ribbonFont);
|
|
}
|
|
else
|
|
{
|
|
QFont font;
|
|
font.fromString(fontStr);
|
|
#ifdef Q_WS_MAC
|
|
// On Mac we need to have bigger fonts
|
|
font.setPointSize(font.pointSize() + 4);
|
|
#endif
|
|
// Now update fonts
|
|
updateFonts(font);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::parseAndCreateChildren(const QDomElement& tabPageElement, QObject* pParent)
|
|
{
|
|
for (QDomElement domElementIterator = tabPageElement.firstChildElement(); !domElementIterator.isNull(); domElementIterator = domElementIterator.nextSiblingElement())
|
|
{
|
|
#ifdef Q_WS_MAC
|
|
if (domElementIterator.attribute(attribWinOnly) == "true")
|
|
continue;
|
|
#else
|
|
if (domElementIterator.attribute(attribMacOnly) == "true")
|
|
continue;
|
|
#endif
|
|
//check if we need to enable any element via fflag?
|
|
QString flagName = domElementIterator.attribute(attribFFlag);
|
|
if (!flagName.isEmpty())
|
|
{
|
|
try
|
|
{
|
|
bool negate = flagName.startsWith("!");
|
|
if (negate)
|
|
flagName = flagName.mid(1);
|
|
|
|
bool keepItem = RBX::GlobalAdvancedSettings::singleton()->getFFlag(flagName.toStdString());
|
|
if (negate)
|
|
keepItem = !keepItem;
|
|
|
|
if (!keepItem)
|
|
continue;
|
|
}
|
|
catch (const std::runtime_error&)
|
|
{
|
|
RBXASSERT(false);
|
|
}
|
|
}
|
|
|
|
QObject* pCreatedElement = NULL;
|
|
QString domElementTagName = domElementIterator.tagName().toLower();
|
|
|
|
if (domElementTagName == elemTabPage)
|
|
{
|
|
pCreatedElement = parseAndCreateRibbonPage(domElementIterator);
|
|
}
|
|
else if (domElementTagName == elemGroup)
|
|
{
|
|
pCreatedElement = parseAndCreateRibbonGroup(domElementIterator, pParent);
|
|
}
|
|
else if (domElementTagName == elemColorPicker)
|
|
{
|
|
pCreatedElement = parseAndCreateColorPicker(domElementIterator, pParent);
|
|
}
|
|
else if (domElementTagName == elemMenuPopupButton)
|
|
{
|
|
pCreatedElement = parseAndCreateMenuPopupButton(domElementIterator, pParent);
|
|
}
|
|
else if (domElementTagName == elemMenuButton)
|
|
{
|
|
pCreatedElement = parseAndCreateMenu(domElementIterator, pParent);
|
|
}
|
|
else if (domElementTagName == elemSplitButton)
|
|
{
|
|
pCreatedElement = parseAndCreateSplitButton(domElementIterator, pParent);
|
|
}
|
|
else if (domElementTagName == elemRibbonGallery)
|
|
{
|
|
pCreatedElement = parseAndCreateRibbonGallery(domElementIterator, pParent);
|
|
}
|
|
else if (domElementTagName == elemAddAction)
|
|
{
|
|
QString actionType = domElementIterator.attribute(attribType);
|
|
if (actionType == actionCheckBox)
|
|
{
|
|
pCreatedElement = parseAndCreateCheckBox(domElementIterator, pParent);
|
|
}
|
|
else if (actionType == actionRadio)
|
|
{
|
|
pCreatedElement = parseAndCreateRadioButton(domElementIterator, pParent);
|
|
}
|
|
else if ((actionType == actionPlugin) || actionType.isNull())
|
|
{
|
|
pCreatedElement = parseAndCreateRibbonAction(domElementIterator, pParent);
|
|
}
|
|
else
|
|
{
|
|
throw RBX::runtime_error("Invalid action type: %s", actionType.toStdString().c_str());
|
|
}
|
|
}
|
|
else if (domElementTagName == elemAddSeparator)
|
|
{
|
|
pCreatedElement = parseAndCreateSeparator(domElementIterator, pParent);
|
|
}
|
|
else if (domElementTagName == elemActionGroup)
|
|
{
|
|
pCreatedElement = parseAndCreateActionGroup(domElementIterator, pParent);
|
|
}
|
|
else if (domElementTagName == elemButtonGroup)
|
|
{
|
|
pCreatedElement = parseAnCreateButtonGroup(domElementIterator, dynamic_cast<QWidget*>(pParent));
|
|
}
|
|
else if (domElementTagName == elemAddRadioButton)
|
|
{
|
|
pCreatedElement = parseAndCreateRadioButton(domElementIterator, pParent);
|
|
}
|
|
else if (domElementTagName == elemGalleryItem)
|
|
{
|
|
parseAndCreateGalleryItem(domElementIterator, pParent);
|
|
}
|
|
else if (domElementTagName == elemAddCheckBox)
|
|
{
|
|
pCreatedElement = parseAndCreateCheckBox(domElementIterator, pParent, false);
|
|
}
|
|
else if (domElementTagName == elemAddComboBox)
|
|
{
|
|
pCreatedElement = parseAndCreateComboBox(domElementIterator, pParent);
|
|
}
|
|
else if (domElementTagName == elemGroupOptionsMenu)
|
|
{
|
|
pCreatedElement = parseAndCreateGroupOptionsMenu(domElementIterator, pParent);
|
|
}
|
|
else if (domElementTagName == elemSubMenu)
|
|
{
|
|
pCreatedElement = parseAndCreateSubMenu(domElementIterator, pParent);
|
|
}
|
|
|
|
// add dynamic properties if any
|
|
if (pCreatedElement)
|
|
{
|
|
if (!domElementIterator.attribute(attribValue).isEmpty())
|
|
pCreatedElement->setProperty("user_value", domElementIterator.attribute(attribValue));
|
|
|
|
if (!domElementIterator.attribute(attribVisible).isEmpty())
|
|
pCreatedElement->setProperty(attribVisible, domElementIterator.attribute(attribVisible) == "true");
|
|
|
|
if (!domElementIterator.attribute(attribEnabled).isEmpty())
|
|
pCreatedElement->setProperty(attribEnabled, domElementIterator.attribute(attribEnabled) == "true");
|
|
|
|
if (!domElementIterator.attribute(attribRequiresAuth).isEmpty())
|
|
pCreatedElement->setProperty(attribRequiresAuth, domElementIterator.attribute(attribRequiresAuth) == "true");
|
|
}
|
|
}
|
|
}
|
|
|
|
///// Create ribbon element functions /////
|
|
RibbonPage* RobloxRibbonMainWindow::parseAndCreateRibbonPage(const QDomElement& domElement)
|
|
{
|
|
// See if it exists first
|
|
QString objectName = getName(domElement);
|
|
RBXASSERT(!objectName.isEmpty());
|
|
|
|
QString pageTitle = getText(domElement);
|
|
if (pageTitle.isNull())
|
|
pageTitle = objectName;
|
|
|
|
RibbonPage* pRibbonPage = createRibbonPage(objectName, pageTitle);
|
|
|
|
// Check to see if this is a context tab
|
|
QString contextTitle = domElement.attribute(attribContextTitle);
|
|
if (!contextTitle.isEmpty())
|
|
{
|
|
pRibbonPage->setContextTitle(contextTitle);
|
|
//(http://www.devmachines.com/productdocs/ribbon/qtitan.html#ContextColor-enum)
|
|
if (!domElement.attribute(attribContextColor).isEmpty())
|
|
pRibbonPage->setContextColor((Qtitan::RibbonPage::ContextColor)domElement.attribute(attribContextColor).toInt());
|
|
}
|
|
|
|
//now recursively create child elements
|
|
parseAndCreateChildren(domElement, pRibbonPage);
|
|
|
|
return pRibbonPage;
|
|
}
|
|
|
|
Qtitan::RibbonGroup* RobloxRibbonMainWindow::parseAndCreateRibbonGroup(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
Qtitan::RibbonPage* pParentPage = dynamic_cast<Qtitan::RibbonPage*>(pParent);
|
|
if (!pParentPage)
|
|
throw RBX::runtime_error("Group parent must be a RibbonPage!");
|
|
|
|
Qtitan::RibbonGroup* pRibbonGroup = createRibbonGroup(pParentPage, getName(domElement));
|
|
RBXASSERT(pRibbonGroup);
|
|
|
|
pRibbonGroup->setBaseSize(getSize(domElement, QSize(100, 50)));
|
|
pRibbonGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
|
|
if (domElement.attribute("options") == "true")
|
|
pRibbonGroup->setOptionButtonVisible();
|
|
|
|
//now recursively create child elements
|
|
parseAndCreateChildren(domElement, pRibbonGroup);
|
|
|
|
return pRibbonGroup;
|
|
}
|
|
|
|
QWidget* RobloxRibbonMainWindow::parseAndCreateColorPicker(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
Qtitan::RibbonGroup* pRibbonGroup = qobject_cast<Qtitan::RibbonGroup*>(pParent);
|
|
if (!pRibbonGroup)
|
|
throw RBX::runtime_error("Menu buttons must have a ribbon group parent element!");
|
|
|
|
QString text = getText(domElement);
|
|
QIcon icon = getIcon(domElement);
|
|
if (icon.isNull() && text.isEmpty())
|
|
throw RBX::runtime_error("Colorpicker must have an icon or text!");
|
|
|
|
QMenu* pDummyMenu = new QMenu;
|
|
connect(pDummyMenu, SIGNAL(aboutToShow ()), this, SLOT(handleColorPickMenu()));
|
|
|
|
QAction* pAction = getAction(domElement);
|
|
if (!pAction)
|
|
throw RBX::runtime_error("Failed to create action: %s", getName(domElement).toStdString().c_str());
|
|
QAction* pAddedAction = pRibbonGroup->addAction(pAction, getStyle(domElement), pDummyMenu);
|
|
pAddedAction->setIcon(icon);
|
|
pAddedAction->setText(text);
|
|
pAddedAction->setObjectName(getName(domElement));
|
|
pAddedAction->setToolTip(getToolTip(domElement));
|
|
|
|
QLayoutItem* pLayoutItem = pRibbonGroup->layout()->itemAt(pRibbonGroup->layout()->count()-1);
|
|
QToolButton* pToolButton = qobject_cast<QToolButton*>(pLayoutItem ? pLayoutItem->widget() : NULL);
|
|
if (pToolButton)
|
|
{
|
|
if (!domElement.attribute(attribCheckable).isEmpty() && (domElement.attribute(attribCheckable) == "true"))
|
|
{
|
|
pToolButton->defaultAction()->setObjectName(getName(domElement));
|
|
pToolButton->defaultAction()->setCheckable(true);
|
|
pToolButton->setCheckable(true);
|
|
}
|
|
pDummyMenu->setParent(pToolButton);
|
|
pToolButton->setObjectName(getName(domElement));
|
|
connect(pToolButton, SIGNAL(clicked()), this, SLOT(handleToolButtonClicked()));
|
|
}
|
|
|
|
return pToolButton;
|
|
}
|
|
|
|
QMenu* RobloxRibbonMainWindow::parseAndCreateMenuPopupButton(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
Qtitan::RibbonGroup* pRibbonGroup = qobject_cast<Qtitan::RibbonGroup*>(pParent);
|
|
if (!pRibbonGroup)
|
|
throw RBX::runtime_error("Menu buttons must have a ribbon group parent element!");
|
|
|
|
QString text = getText(domElement);
|
|
QIcon icon = getIcon(domElement);
|
|
if (icon.isNull() && text.isEmpty())
|
|
throw RBX::runtime_error("Drop downs must have an icon or text!");
|
|
|
|
Qt::ToolButtonStyle style = getStyle(domElement);
|
|
QToolButton::ToolButtonPopupMode popupMode = getPopupStyle(domElement);
|
|
|
|
QString objectName = getName(domElement);
|
|
ToolButtonProxyMenu* menu = new ToolButtonProxyMenu(objectName, pRibbonGroup);
|
|
QAction* action = menu->menuAction();
|
|
action->setText(text);
|
|
|
|
if (menu->selectChangesText &&
|
|
!domElement.attribute(attribSelectChangesText).isEmpty() &&
|
|
domElement.attribute(attribSelectChangesText) == "true")
|
|
{
|
|
menu->selectChangesText = true;
|
|
}
|
|
|
|
action->setIcon(icon);
|
|
action->setToolTip(getToolTip(domElement));
|
|
|
|
pRibbonGroup->addAction(action);
|
|
|
|
QString settingName;
|
|
|
|
if (domElement.attribute(attribSetting) == "true")
|
|
{
|
|
settingName = objectName + "_RibbonSetting";
|
|
}
|
|
|
|
QLayoutItem* item = pRibbonGroup->layout()->itemAt(pRibbonGroup->layout()->count()-1);
|
|
if (QToolButton* widgetAction = qobject_cast<QToolButton*>(item ? item->widget() : Q_NULL))
|
|
{
|
|
if (!domElement.attribute(attribCheckable).isEmpty() && (domElement.attribute(attribCheckable) == "true"))
|
|
{
|
|
widgetAction->defaultAction()->setObjectName(getName(domElement));
|
|
widgetAction->defaultAction()->setCheckable(true);
|
|
widgetAction->setCheckable(true);
|
|
}
|
|
widgetAction->setToolButtonStyle(style);
|
|
widgetAction->setPopupMode(popupMode);
|
|
connect(widgetAction, SIGNAL(clicked()), this, SLOT(handleToolButtonClicked()));
|
|
widgetAction->setObjectName(objectName);
|
|
menu->toolButton = widgetAction;
|
|
menu->settingName = settingName;
|
|
}
|
|
|
|
menu->setObjectName(objectName);
|
|
menu->menuAction()->setObjectName(objectName);
|
|
|
|
// check if we need to handle menu execution at top level
|
|
if (!domElement.attribute(attribMenuTrigger).isEmpty() && domElement.attribute(attribMenuTrigger) == "true")
|
|
connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(handleActionSelected(QAction*)));
|
|
|
|
// now recursively create child elements
|
|
parseAndCreateChildren(domElement, menu);
|
|
|
|
QString value = domElement.attribute(attribValue);
|
|
|
|
if (!settingName.isEmpty())
|
|
{
|
|
value = RobloxSettings().value(settingName, value).toString();
|
|
}
|
|
|
|
action->setProperty("selectedItem", value);
|
|
|
|
QList<QAction*> actions = menu->actions();
|
|
for (QList<QAction*>::iterator iter = actions.begin();
|
|
iter != actions.end();
|
|
iter++)
|
|
{
|
|
if ((*iter)->objectName() == value)
|
|
{
|
|
menu->toolButton->connect(menu->toolButton, SIGNAL(clicked()), *iter, SLOT(trigger()));
|
|
menu->connect(*iter, SIGNAL(changed()), menu, SLOT(onChanged()));
|
|
menu->boundAction = *iter;
|
|
|
|
QString iconPath = (*iter)->property("iconPath").toString();
|
|
QSize iconSize = menu->toolButton->icon().availableSizes().first();
|
|
|
|
menu->toolButton->defaultAction()->setIcon(QIcon(QPixmap(iconPath).scaled(iconSize)));
|
|
|
|
if (!settingName.isEmpty())
|
|
{
|
|
RobloxSettings().setValue(settingName, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
return menu;
|
|
}
|
|
|
|
void ToolButtonProxyMenu::onChanged()
|
|
{
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
|
|
if (action->isEnabled() != toolButton->isEnabled())
|
|
toolButton->setEnabled(action->isEnabled());
|
|
}
|
|
|
|
QMenu* RobloxRibbonMainWindow::parseAndCreateMenu(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
Qtitan::RibbonGroup* pRibbonGroup = qobject_cast<Qtitan::RibbonGroup*>(pParent);
|
|
if (!pRibbonGroup)
|
|
throw RBX::runtime_error("Menu buttons must have a ribbon group parent element!");
|
|
|
|
QString text = getText(domElement);
|
|
QIcon icon = getIcon(domElement);
|
|
if ( icon.isNull() && text.isEmpty() )
|
|
throw RBX::runtime_error("Drop downs must have an icon or text!");
|
|
|
|
QMenu* pMenu = pRibbonGroup->addMenu(icon, text, getStyle(domElement));
|
|
connect(pMenu, SIGNAL(aboutToShow()), &UpdateUIManager::Instance(), SLOT(onMenuShow()));
|
|
|
|
RBXASSERT(pMenu);
|
|
RBXASSERT(pMenu->menuAction());
|
|
|
|
QString objectName = getName(domElement);
|
|
pMenu->setObjectName(objectName);
|
|
pMenu->menuAction()->setToolTip(getToolTip(domElement));
|
|
pMenu->menuAction()->setObjectName(objectName);
|
|
|
|
// check if we need to handle menu execution at top level
|
|
if (!domElement.attribute(attribMenuTrigger).isEmpty() && domElement.attribute(attribMenuTrigger) == "true")
|
|
connect(pMenu, SIGNAL(triggered(QAction*)), this, SLOT(handleMenuActionTrigger(QAction*)));
|
|
|
|
// now recursively create child elements
|
|
if (objectName == "emulateDeviceAction")
|
|
StudioDeviceEmulator::Instance().addEmulationChildren(this, pMenu);
|
|
|
|
parseAndCreateChildren(domElement, pMenu);
|
|
|
|
if ( !domElement.attribute(attribCheckable).isEmpty() )
|
|
pMenu->menuAction()->setCheckable(domElement.attribute(attribCheckable) == "true");
|
|
|
|
if (domElement.attribute(attribIconVisibleInMenu) == "false")
|
|
{
|
|
QList<QAction*> actions = pMenu->actions();
|
|
for (int ii = 0; ii < actions.size(); ii++)
|
|
actions[ii]->setIconVisibleInMenu(false);
|
|
}
|
|
|
|
return pMenu;
|
|
}
|
|
|
|
Qtitan::RibbonGallery* RobloxRibbonMainWindow::parseAndCreateRibbonGallery(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
Qtitan::RibbonGroup* pRibbonGroup = qobject_cast<Qtitan::RibbonGroup*>(pParent);
|
|
if (!pRibbonGroup)
|
|
throw RBX::runtime_error("Gallery parent must be a ribbon group!");
|
|
|
|
Qtitan::RibbonGallery* pGallery = new Qtitan::RibbonGallery;
|
|
pGallery->setObjectName(getName(domElement));
|
|
pGallery->setScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
|
|
|
Qtitan::RibbonGalleryGroup* pGalleryGroup = Qtitan::RibbonGalleryGroup::createGalleryGroup();
|
|
pGalleryGroup->setClipItems(false);
|
|
|
|
// Set up the size of each color spot and populate colors
|
|
QSize itemSize(domElement.attribute(attribItemWidth).toInt(), domElement.attribute(attribItemHeight).toInt());
|
|
pGalleryGroup->setSize(itemSize.isNull() ? QSize(16, 16) : itemSize);
|
|
|
|
pGallery->setGalleryGroup(pGalleryGroup);
|
|
|
|
// check if we need to create color gallery (special case)
|
|
if (domElement.attribute(attribType) == "color")
|
|
{
|
|
GalleryItemColor::addStandardColors(pGalleryGroup);
|
|
pGallery->setCheckedIndex(12);
|
|
}
|
|
else
|
|
{
|
|
parseAndCreateChildren(domElement, pGalleryGroup);
|
|
pGallery->setCheckedIndex(0);
|
|
}
|
|
|
|
RBXASSERT(pGalleryGroup->getItemCount());
|
|
|
|
int numColumns = domElement.attribute(attribColumns).toInt();
|
|
if (!numColumns)
|
|
numColumns = 5;
|
|
|
|
int galleryWidth = (itemSize.width() * numColumns);
|
|
int galleryHeight = (qCeil(pGalleryGroup->getItemCount()/(float)numColumns)*itemSize.height());
|
|
|
|
if (!domElement.attribute(attribPopup).isEmpty() && domElement.attribute(attribPopup).toLower() == "false")
|
|
{
|
|
if (galleryHeight > 64)
|
|
{
|
|
galleryHeight = 64;
|
|
galleryWidth = galleryWidth + qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
|
|
|
|
pGallery->setScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
|
}
|
|
|
|
pGallery->setBaseSize(QSize(galleryWidth, galleryHeight));
|
|
pRibbonGroup->addWidget(pGallery);
|
|
}
|
|
else
|
|
{
|
|
pGallery->setBaseSize(QSize(galleryWidth+4, galleryHeight+2));
|
|
|
|
OfficePopupMenu* pPopupMenu = OfficePopupMenu::createPopupMenu(ribbonBar());
|
|
pPopupMenu->addWidget(pGallery);
|
|
|
|
QAction* pAction = pRibbonGroup->addAction(getIcon(domElement), getText(domElement), getStyle(domElement), pPopupMenu, QToolButton::InstantPopup);
|
|
pAction->setObjectName(getName(domElement));
|
|
}
|
|
|
|
connect(pGallery, SIGNAL(itemClicked(RibbonGalleryItem*)), this, SLOT(handleBasicObjectsGalleryItemClicked(RibbonGalleryItem*)));
|
|
|
|
return pGallery;
|
|
}
|
|
|
|
Qtitan::RibbonGalleryItem* RobloxRibbonMainWindow::parseAndCreateGalleryItem(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
Qtitan::RibbonGalleryGroup* pGalleryGroup = qobject_cast<Qtitan::RibbonGalleryGroup*>(pParent);
|
|
if (!pGalleryGroup)
|
|
throw RBX::runtime_error("galleryitem must be beneath a gallery group");
|
|
|
|
Qtitan::RibbonGalleryItem* pGalleryItem = new StudioGalleryItem;
|
|
|
|
pGalleryItem->setCaption(getText(domElement));
|
|
pGalleryItem->setToolTip(getToolTip(domElement));
|
|
pGalleryItem->setSizeHint(pGalleryGroup->getSize());
|
|
pGalleryItem->setData(GALLERY_ITEM_USER_DATA, domElement.attribute(attribValue));
|
|
|
|
QIcon icon = getIcon(domElement);
|
|
if (!icon.isNull())
|
|
pGalleryItem->setIcon(icon);
|
|
|
|
pGalleryGroup->appendItem(pGalleryItem);
|
|
|
|
return pGalleryItem;
|
|
}
|
|
|
|
QActionGroup* RobloxRibbonMainWindow::parseAndCreateActionGroup(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
Qtitan::RibbonGroup* pGroup = qobject_cast<Qtitan::RibbonGroup*>(pParent);
|
|
QMenu* pMenu = qobject_cast<QMenu*>(pParent);
|
|
if (!pGroup && !pMenu)
|
|
throw RBX::runtime_error("actiongroup elements must be beneath a group or menu");
|
|
|
|
// Qt doesn't let us add same actions in multiple action groups, so we must reuse the already created action group
|
|
QString objectName = getName(domElement);
|
|
QActionGroup* pActionGroup = findChild<QActionGroup*>(objectName);
|
|
|
|
if (!pActionGroup)
|
|
{
|
|
pActionGroup = new QActionGroup(this);
|
|
pActionGroup->setObjectName(objectName);
|
|
|
|
bool isSingleSelect = domElement.attribute(attribType) == "singleselect";
|
|
pActionGroup->setExclusive(!isSingleSelect);
|
|
pActionGroup->setProperty("singleselect", isSingleSelect);
|
|
|
|
connect(pActionGroup, SIGNAL(selected(QAction*)), this, SLOT(handleActionGroupSelected(QAction*)));
|
|
}
|
|
|
|
// we will be parsing actions to a dummy action group and later add the created actions to the original action group
|
|
QActionGroup dummyActionGroup(this);
|
|
//now recursively create child elements
|
|
parseAndCreateChildren(domElement, &dummyActionGroup);
|
|
|
|
bool isCheckable = domElement.attribute(attribCheckable) == "true";
|
|
|
|
QAction* pAction = NULL;
|
|
QList<QAction*> actions = dummyActionGroup.actions();
|
|
for (int ii = 0; ii < actions.size(); ii++)
|
|
{
|
|
pAction = actions.at(ii);
|
|
pAction->setCheckable(isCheckable);
|
|
// add action to correct parent i.e. Ribbon Group or Menu
|
|
if (pGroup)
|
|
pGroup->addAction(pAction, getStyle(domElement));
|
|
else
|
|
pMenu->addAction(pAction);
|
|
// add actions to the original group (NOTE: dummy action group will not be having these actions anymore after this)
|
|
pActionGroup->addAction(pAction);
|
|
}
|
|
|
|
return pActionGroup;
|
|
}
|
|
|
|
QButtonGroup* RobloxRibbonMainWindow::parseAnCreateButtonGroup(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
Qtitan::RibbonGroup* pRibbonGroup = qobject_cast<Qtitan::RibbonGroup*>(pParent);
|
|
if (!pRibbonGroup)
|
|
throw RBX::runtime_error("buttongroup must have a ribbon group parent element!");
|
|
|
|
QButtonGroup* pButtonGroup = new QButtonGroup(this);
|
|
pButtonGroup->setObjectName(getName(domElement));
|
|
|
|
parseAndCreateChildren(domElement, pButtonGroup);
|
|
|
|
// add created buttons to ribbon group
|
|
QList<QAbstractButton*> buttons = pButtonGroup->buttons();
|
|
if (buttons.size() > 0)
|
|
{
|
|
for (int ii = 0; ii < buttons.size(); ++ii)
|
|
{
|
|
// populate default value if any
|
|
if (buttons.at(ii)->isChecked())
|
|
populateNameValueStore(pButtonGroup->objectName(), buttons.at(ii));
|
|
// add widget to ribbon group
|
|
pRibbonGroup->addWidget(buttons.at(ii));
|
|
}
|
|
|
|
connect(pButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(handleButtonGroupSelected(QAbstractButton*)));
|
|
}
|
|
|
|
return pButtonGroup;
|
|
}
|
|
|
|
QAction* RobloxRibbonMainWindow::parseAndCreateSplitButton(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
Qtitan::RibbonGroup* pRibbonGroup = qobject_cast<Qtitan::RibbonGroup*>(pParent);
|
|
if (!pRibbonGroup)
|
|
throw RBX::runtime_error("splitbutton must have a ribbon group parent element!");
|
|
|
|
QMenu* pMenu = new QMenu(this);
|
|
parseAndCreateChildren(domElement, pMenu);
|
|
|
|
QAction* pAction = pRibbonGroup->addAction(getIcon(domElement), getText(domElement), getStyle(domElement), pMenu);
|
|
pAction->setObjectName(getName(domElement));
|
|
if (!FFlag::StudioSeparateActionByActivationMethod)
|
|
connect(pAction, SIGNAL(triggered(bool)), this, SLOT(commonSlot(bool)));
|
|
|
|
return pAction;
|
|
}
|
|
|
|
QAction* RobloxRibbonMainWindow::parseAndCreateSeparator(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
if (QMenu* pParentMenu = qobject_cast<QMenu*>(pParent))
|
|
return pParentMenu->addSeparator();
|
|
|
|
if (Qtitan::RibbonGroup* pParentGroup = qobject_cast<Qtitan::RibbonGroup*>(pParent))
|
|
return pParentGroup->addSeparator();
|
|
|
|
//none of the above - throw
|
|
throw RBX::runtime_error("addseparator elements must be beneath a menu or group!");
|
|
}
|
|
|
|
QRadioButton* RobloxRibbonMainWindow::parseAndCreateRadioButton(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
Qtitan::RibbonGroup* pRibbonGroup = qobject_cast<Qtitan::RibbonGroup*>(pParent);
|
|
QButtonGroup* pButtonGroup = qobject_cast<QButtonGroup*>(pParent);
|
|
if (!pRibbonGroup && !pButtonGroup)
|
|
throw RBX::runtime_error("radio buttons must be beneath a button/ribbon group");
|
|
|
|
QString objectName = getName(domElement);
|
|
if (objectName.isEmpty())
|
|
throw RBX::runtime_error("radio button name can not be empty!");
|
|
|
|
// Create the button
|
|
QRadioButton* pRadioButton = new QRadioButton;
|
|
pRadioButton->setObjectName(objectName);
|
|
pRadioButton->setToolTip(getToolTip(domElement));
|
|
pRadioButton->setText(getText(domElement));
|
|
pRadioButton->setChecked(domElement.attribute(attribChecked) == "true");
|
|
|
|
if (pRibbonGroup)
|
|
addWidgetToRibbonGroup(pRibbonGroup, pRadioButton);
|
|
else
|
|
addButtonToButtonGroup(pButtonGroup, pRadioButton);
|
|
|
|
QAction* pAction = getAction(domElement);
|
|
if (!pAction)
|
|
throw RBX::runtime_error("Failed to create action: %s", getName(domElement).toStdString().c_str());
|
|
|
|
connect(pRadioButton, SIGNAL(clicked()), pAction, SLOT(trigger()));
|
|
if (!FFlag::StudioSeparateActionByActivationMethod)
|
|
connect(pAction, SIGNAL(triggered()), pRadioButton, SLOT(toggle()));
|
|
|
|
return pRadioButton;
|
|
}
|
|
|
|
QCheckBox* RobloxRibbonMainWindow::parseAndCreateCheckBox(const QDomElement& domElement, QObject* pParent, bool actionMode)
|
|
{
|
|
Qtitan::RibbonGroup* pRibbonGroup = qobject_cast<Qtitan::RibbonGroup*>(pParent);
|
|
if (!pRibbonGroup)
|
|
throw RBX::runtime_error("checkbox must be beneath a ribbon group");
|
|
|
|
QString objectName = getName(domElement);
|
|
if (objectName.isEmpty())
|
|
throw RBX::runtime_error("checkbox name element can not be empty!");
|
|
|
|
// Create button
|
|
QCheckBox* pCheckBox = new QCheckBox;
|
|
pCheckBox->setObjectName(getName(domElement));
|
|
pCheckBox->setText(getText(domElement));
|
|
pCheckBox->setToolTip(getToolTip(domElement));
|
|
pCheckBox->setChecked(domElement.attribute(attribChecked) == "true");
|
|
|
|
// add to ribbon group
|
|
addWidgetToRibbonGroup(pRibbonGroup, pCheckBox);
|
|
|
|
// if no action is associated then connect with clicked signal to populate valuestore
|
|
if (!actionMode)
|
|
connect(pCheckBox, SIGNAL(clicked(bool)), this, SLOT(handleCheckBoxClicked(bool)));
|
|
|
|
return pCheckBox;
|
|
}
|
|
|
|
QComboBox* RobloxRibbonMainWindow::parseAndCreateComboBox(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
Qtitan::RibbonGroup* pRibbonGroup = qobject_cast<Qtitan::RibbonGroup*>(pParent);
|
|
if (!pRibbonGroup)
|
|
throw RBX::runtime_error("checkbox must be beneath a ribbon group");
|
|
|
|
QString objectName = getName(domElement);
|
|
if (objectName.isEmpty())
|
|
throw RBX::runtime_error("checkbox name element can not be empty!");
|
|
|
|
// Create button
|
|
QComboBox* pComboBox = new QComboBox;
|
|
pComboBox->setObjectName(getName(domElement));
|
|
|
|
// add combobox items
|
|
for (QDomElement domElementIterator = domElement.firstChildElement(); !domElementIterator.isNull(); domElementIterator = domElementIterator.nextSiblingElement())
|
|
{
|
|
if (domElementIterator.tagName().toLower() == elemComboBoxItem)
|
|
{
|
|
pComboBox->addItem(getText(domElementIterator));
|
|
// check if there's any user value
|
|
if (!domElementIterator.attribute(attribValue).isEmpty())
|
|
pComboBox->setItemData(pComboBox->count() - 1, domElementIterator.attribute(attribValue));
|
|
// if this index is default
|
|
if (domElementIterator.attribute(attribDefaultValue) == "true")
|
|
pComboBox->setCurrentIndex(pComboBox->count() - 1);
|
|
}
|
|
}
|
|
|
|
// add to ribbon group
|
|
addWidgetToRibbonGroup(pRibbonGroup, pComboBox);
|
|
|
|
connect(pComboBox, SIGNAL(activated(int)), this, SLOT(handleComboBoxActivated(int)));
|
|
|
|
return pComboBox;
|
|
}
|
|
|
|
QAction* RobloxRibbonMainWindow::parseAndCreateRibbonAction(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
if (!pParent)
|
|
throw RBX::runtime_error("Failed to create action: %s. No parent element provided!", getName(domElement).toStdString().c_str());
|
|
|
|
QAction* pAction = getAction(domElement);
|
|
if (!pAction)
|
|
throw RBX::runtime_error("Failed to create action: %s", getName(domElement).toStdString().c_str());
|
|
|
|
addActionToParent(pAction, pParent, domElement);
|
|
|
|
// If we have verbs that handle these actions, we just need to connect these actions to the common slot,
|
|
// which will invoke the associated verb. In the future we should consider making the verb list in the common
|
|
// slot just a list of strings instead of actual QAction*s. That way we can add these actions to that list
|
|
// instead of marking them as "iscommonslot=true" in the RobloxStudioRibbon.xml file.
|
|
if (!FFlag::StudioSeparateActionByActivationMethod && domElement.attribute(attribCommonSlot) == "true")
|
|
connect(pAction, SIGNAL(triggered(bool)), this, SLOT(commonSlot(bool)));
|
|
|
|
return pAction;
|
|
}
|
|
|
|
QMenu* RobloxRibbonMainWindow::parseAndCreateGroupOptionsMenu(const QDomElement& domElement, QObject* pParent)
|
|
{
|
|
Qtitan::RibbonGroup* pRibbonGroup = qobject_cast<Qtitan::RibbonGroup*>(pParent);
|
|
if (!pRibbonGroup)
|
|
throw RBX::runtime_error("optionsMenu must be beneath a ribbon group");
|
|
|
|
QAction* optionButtonAction = pRibbonGroup->getOptionButtonAction();
|
|
if (!optionButtonAction)
|
|
throw RBX::runtime_error("optionsMenu must has an action to associate");
|
|
|
|
QMenu* pMenu = new QMenu(pRibbonGroup);
|
|
pMenu->setObjectName(QString("__%1Group-Menu_").arg(pRibbonGroup->objectName()));
|
|
parseAndCreateChildren(domElement, pMenu);
|
|
|
|
if (!pMenu->isEmpty())
|
|
{
|
|
pMenu->setProperty("useActionName", true);
|
|
connect(pMenu, SIGNAL(aboutToShow()), &UpdateUIManager::Instance(), SLOT(onMenuShow()));
|
|
|
|
optionButtonAction->setToolTip(getToolTip(domElement));
|
|
connect(optionButtonAction, SIGNAL(triggered()), this, SLOT(showGroupOptionsMenu()));
|
|
|
|
// check if we need to handle menu execution at top level
|
|
if (!domElement.attribute(attribMenuTrigger).isEmpty() && domElement.attribute(attribMenuTrigger) == "true")
|
|
connect(pMenu, SIGNAL(triggered(QAction*)), this, SLOT(handleMenuActionTrigger(QAction*)));
|
|
}
|
|
|
|
return pMenu;
|
|
}
|
|
|
|
QMenu* RobloxRibbonMainWindow::parseAndCreateSubMenu(const QDomElement& domElement, QObject* parent)
|
|
{
|
|
QMenu* menuParent = qobject_cast<QMenu*>(parent);
|
|
if (!menuParent)
|
|
throw RBX::runtime_error("submenu must be beneath a QMenu");
|
|
|
|
QMenu* submenu = new QMenu(domElement.attribute("name"));
|
|
parseAndCreateChildren(domElement, submenu);
|
|
menuParent->addMenu(submenu);
|
|
connect(submenu, SIGNAL(aboutToShow()), &UpdateUIManager::Instance(), SLOT(onMenuShow()));
|
|
return submenu;
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::showGroupOptionsMenu()
|
|
{
|
|
QAction* pSender = qobject_cast<QAction*>(sender());
|
|
if (pSender)
|
|
{
|
|
QWidget* pWidget = pSender->parentWidget();
|
|
if (pWidget)
|
|
{
|
|
QMenu* pMenu = pWidget->findChild<QMenu*>(QString("__%1Group-Menu_").arg(pWidget->objectName()));
|
|
if (pMenu)
|
|
pMenu->exec(QCursor::pos());
|
|
}
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::updateProxyAction()
|
|
{
|
|
QAction* pAction = qobject_cast<QAction*>(sender());
|
|
QMap<QString, QAction*>::iterator iter = m_mProxyActions.find(getFullName(pAction));
|
|
if (iter == m_mProxyActions.end())
|
|
return;
|
|
|
|
iter.value()->setEnabled(pAction->isEnabled());
|
|
iter.value()->setChecked(pAction->isChecked());
|
|
iter.value()->setVisible(pAction->isVisible());
|
|
iter.value()->setIcon(pAction->icon());
|
|
}
|
|
|
|
///// Utility functions /////
|
|
QString RobloxRibbonMainWindow::getName(const QDomElement& domElement) const
|
|
{ return domElement.attribute(attribName); }
|
|
|
|
QString RobloxRibbonMainWindow::getToolTip(const QDomElement& domElement) const
|
|
{
|
|
// if we've tool tip then return
|
|
if (!domElement.attribute(attribToolTip).isEmpty())
|
|
return domElement.attribute(attribToolTip);
|
|
|
|
// return "text"(i.e. title)
|
|
if (!domElement.attribute(attribText).isEmpty())
|
|
return domElement.attribute(attribText);
|
|
|
|
// nothing, return name
|
|
return getName(domElement);
|
|
}
|
|
|
|
QString RobloxRibbonMainWindow::getText(const QDomElement& domElement) const
|
|
{
|
|
if (!domElement.attribute(attribText).isEmpty())
|
|
return domElement.attribute(attribText);
|
|
|
|
return domElement.attribute(attribName);
|
|
}
|
|
|
|
QSize RobloxRibbonMainWindow::getSize(const QDomElement& domElement, const QSize& defaultSize) const
|
|
{
|
|
QSize size(defaultSize);
|
|
|
|
int width = domElement.attribute(attribIconWidth).toInt();
|
|
if (width > 0)
|
|
size.setWidth(width);
|
|
|
|
int height = domElement.attribute(attribIconHeight).toInt();
|
|
if (height > 0)
|
|
size.setHeight(height);
|
|
|
|
return size;
|
|
}
|
|
|
|
QIcon RobloxRibbonMainWindow::getIcon(const QDomElement& domElement, const QSize& defaultSize) const
|
|
{
|
|
QSize size(getSize(domElement, defaultSize));
|
|
|
|
QPixmap pixmap(domElement.attribute(attribIcon));
|
|
if (!pixmap.isNull())
|
|
pixmap = pixmap.scaled(size);
|
|
|
|
return QIcon(pixmap);
|
|
}
|
|
|
|
Qt::ToolButtonStyle RobloxRibbonMainWindow::getStyle( const QDomElement& domElement ) const
|
|
{
|
|
QString style = domElement.attribute(attribStyle);
|
|
if (style == "icononly")
|
|
return Qt::ToolButtonIconOnly;
|
|
if (style == "textonly")
|
|
return Qt::ToolButtonTextOnly;
|
|
if (style == "textbesideicon")
|
|
return Qt::ToolButtonTextBesideIcon;
|
|
if (style == "textundericon")
|
|
return Qt::ToolButtonTextUnderIcon;
|
|
return Qt::ToolButtonFollowStyle;
|
|
}
|
|
|
|
enum QToolButton::ToolButtonPopupMode RobloxRibbonMainWindow::getPopupStyle(const QDomElement& domElement) const
|
|
{
|
|
QString style = domElement.attribute(attribPopupStyle);
|
|
if (style == "delayed")
|
|
return QToolButton::DelayedPopup;
|
|
if (style == "menubutton")
|
|
return QToolButton::MenuButtonPopup;
|
|
if (style == "instant")
|
|
return QToolButton::InstantPopup;
|
|
|
|
return QToolButton::InstantPopup;
|
|
}
|
|
|
|
|
|
QAction* RobloxRibbonMainWindow::getAction(const QDomElement& domElement)
|
|
{
|
|
QString objectName = getName(domElement);
|
|
if (objectName.isEmpty())
|
|
return NULL;
|
|
|
|
QAction* pAction = findChildAction(objectName);
|
|
if (!pAction)
|
|
{
|
|
if (domElement.attribute(attribType) == "plugin")
|
|
{
|
|
pAction = new PluginAction(getText(domElement), this);
|
|
}
|
|
else
|
|
{
|
|
pAction = new QAction(getText(domElement), this);
|
|
}
|
|
|
|
// add newly created actions to mainwindow, so it is available for customization
|
|
addAction(pAction);
|
|
|
|
pAction->setObjectName(objectName);
|
|
pAction->setEnabled(true);
|
|
pAction->setVisible(true);
|
|
}
|
|
|
|
if ( !domElement.attribute(attribText).isEmpty() )
|
|
pAction->setText(domElement.attribute(attribText));
|
|
|
|
if ( !domElement.attribute(attribIcon).isEmpty())
|
|
pAction->setProperty("iconPath", domElement.attribute(attribIcon));
|
|
|
|
QIcon icon = getIcon(domElement);
|
|
if (!icon.isNull())
|
|
pAction->setIcon(icon);
|
|
|
|
if ( !domElement.attribute(attribToolTip).isEmpty() )
|
|
pAction->setToolTip(domElement.attribute(attribToolTip));
|
|
|
|
if ( !domElement.attribute(attribInstanceName).isEmpty() )
|
|
pAction->setData(domElement.attribute(attribInstanceName));
|
|
|
|
if ( !domElement.attribute(attribCheckable).isEmpty() )
|
|
pAction->setCheckable(domElement.attribute(attribCheckable) == "true");
|
|
|
|
if (domElement.attribute(attribType) == "plugin")
|
|
{
|
|
RobloxQuickAccessConfig& quickAccessConfig = RobloxQuickAccessConfig::singleton();
|
|
if (QAction* mappedAction = quickAccessConfig.mapProxyAction(pAction))
|
|
quickAccessConfig.addToQuickAccessBar(mappedAction, quickAccessConfig.visibleInQuickAccessBar(pAction));
|
|
}
|
|
|
|
if (domElement.attribute(attribProxyAction) == "true")
|
|
{
|
|
// since this action is a proxy action, it's source action will be added to quick access dialog
|
|
pAction->setProperty("disableForQuickAccess", true);
|
|
m_mProxyActions.insert(objectName, pAction);
|
|
}
|
|
|
|
return pAction;
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::addWidgetToRibbonGroup(Qtitan::RibbonGroup* pRibbonGroup, QWidget* pWidget)
|
|
{
|
|
if (!pRibbonGroup || !pWidget)
|
|
return;
|
|
|
|
QAction* pAction = pRibbonGroup->addWidget(pWidget);
|
|
pAction->setObjectName(pWidget->objectName());
|
|
pAction->setCheckable(true);
|
|
|
|
QAction* pMainWindowAction = findChildAction(pWidget->objectName());
|
|
if (pMainWindowAction)
|
|
{
|
|
/*
|
|
* This sets up bidirectional trigger handling to make sure menu actions are in sync with existing action.
|
|
* This is because we have to have separate actions for displaying some buttons on the ribbon bar than
|
|
* the existing action that actually handles the execution.
|
|
*/
|
|
m_mCorrelaryActionMap.insert(pMainWindowAction, pWidget);
|
|
|
|
//execute action
|
|
if (qobject_cast<QAbstractButton*>(pWidget))
|
|
connect(pWidget, SIGNAL(clicked(bool)), pMainWindowAction, SLOT(trigger()));
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::addButtonToButtonGroup(QButtonGroup* pButtonGroup, QAbstractButton* pButton)
|
|
{
|
|
// add button the ribbon group
|
|
pButtonGroup->addButton(pButton);
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::addActionToParent(QAction* pAction, QObject* pParent, const QDomElement& domElement)
|
|
{
|
|
if ( Qtitan::RibbonGallery* pRibbonGallery = qobject_cast<Qtitan::RibbonGallery*>(pParent) )
|
|
{
|
|
RibbonGalleryGroup* pRibbonGalleryGroup = pRibbonGallery->galleryGroup();
|
|
if ( !pRibbonGalleryGroup )
|
|
throw RBX::runtime_error("Failed to create action: %s. RibbonGallery must have RibbonGalleryGroup!", qPrintable(pAction->objectName()));
|
|
|
|
RibbonGalleryItem* pGalleryItem = pRibbonGalleryGroup->addItem(pAction->text(), pAction->icon().pixmap(getSize(domElement)));
|
|
pGalleryItem->setData(0,pAction->data());
|
|
pGalleryItem->setToolTip(pAction->text());
|
|
pGalleryItem->setStatusTip(pAction->text());
|
|
}
|
|
else if (Qtitan::RibbonGroup* ribbonGroup = qobject_cast<Qtitan::RibbonGroup*>(pParent))
|
|
{
|
|
ribbonGroup->addAction(pAction, getStyle(domElement));
|
|
}
|
|
else if (QWidget* pParentWidget = qobject_cast<QWidget*>(pParent))
|
|
{
|
|
pParentWidget->addAction(pAction);
|
|
}
|
|
else if (QActionGroup* pParentActionGroup = qobject_cast<QActionGroup*>(pParent))
|
|
{
|
|
pParentActionGroup->addAction(pAction);
|
|
}
|
|
else
|
|
{
|
|
pAction->setParent(pParent);
|
|
}
|
|
}
|
|
|
|
///// Slots /////
|
|
void RobloxRibbonMainWindow::handleActionGroupSelected(QAction *pAction)
|
|
{
|
|
const QActionGroup* pActionGroup = qobject_cast<const QActionGroup*>(sender());
|
|
if (!pActionGroup)
|
|
return;
|
|
|
|
//make sure we have only one action is selected
|
|
if (pActionGroup->property("singleselect").toBool())
|
|
{
|
|
QList<QAction*> actions = pActionGroup->actions();
|
|
for (int i = 0; i < actions.size(); i++)
|
|
{
|
|
|
|
if (actions.at(i) != pAction)
|
|
actions.at(i)->setChecked(false);
|
|
}
|
|
}
|
|
|
|
//now execute the action
|
|
if (RobloxDocManager::Instance().getCurrentDoc())
|
|
{
|
|
//set the dynamic properties for later use only if the action is checked
|
|
if (!pAction->isCheckable() || (pAction->isCheckable() && pAction->isChecked()))
|
|
populateNameValueStore(pActionGroup->objectName(), pAction);
|
|
else
|
|
NameValueStoreManager::singleton().clear(pActionGroup->objectName());
|
|
|
|
RobloxDocManager::Instance().getCurrentDoc()->handleAction(pActionGroup->objectName(), pAction->isChecked());
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::handleButtonGroupSelected(QAbstractButton* pButton)
|
|
{
|
|
const QButtonGroup* pButtonGroup = qobject_cast<const QButtonGroup*>(sender());
|
|
if (!pButtonGroup)
|
|
return;
|
|
|
|
//now execute the button
|
|
if (RobloxDocManager::Instance().getCurrentDoc())
|
|
{
|
|
populateNameValueStore(pButtonGroup->objectName(), pButton);
|
|
RobloxDocManager::Instance().getCurrentDoc()->handleAction(pButtonGroup->objectName(), pButton->isChecked());
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::handleActionSelected(QAction* pAction)
|
|
{
|
|
ToolButtonProxyMenu* pMenu = qobject_cast<ToolButtonProxyMenu*>(sender());
|
|
|
|
if (!pMenu)
|
|
return;
|
|
|
|
QToolButton* toolButton = pMenu->toolButton;
|
|
|
|
if (pMenu->boundAction)
|
|
{
|
|
toolButton->disconnect(toolButton, SIGNAL(clicked()), pMenu->boundAction, SLOT(trigger()));
|
|
pMenu->disconnect(pMenu->boundAction, SIGNAL(changed()), pMenu, SLOT(onChanged()));
|
|
}
|
|
|
|
pMenu->toolButton->connect(pMenu->toolButton, SIGNAL(clicked()), pAction, SLOT(trigger()));
|
|
pMenu->connect(pAction, SIGNAL(changed()), pMenu, SLOT(onChanged()));
|
|
|
|
pMenu->boundAction = pAction;
|
|
|
|
if (!pMenu->settingName.isEmpty())
|
|
{
|
|
RobloxSettings().setValue(pMenu->settingName, pAction->objectName());
|
|
}
|
|
|
|
QString iconPath = pAction->property("iconPath").toString();
|
|
|
|
if (!pMenu->toolButton->icon().isNull())
|
|
{
|
|
QSize iconSize = pMenu->toolButton->icon().availableSizes().first();
|
|
pMenu->toolButton->defaultAction()->setIcon(QIcon(QPixmap(iconPath).scaled(iconSize)));
|
|
}
|
|
|
|
if (pMenu->selectChangesText)
|
|
pMenu->toolButton->setText(pAction->text());
|
|
|
|
//now execute the button
|
|
if (RobloxDocManager::Instance().getCurrentDoc())
|
|
{
|
|
populateNameValueStore(pMenu->objectName(), pAction);
|
|
RobloxDocManager::Instance().getCurrentDoc()->handleAction(pMenu->objectName(), pAction->isChecked());
|
|
UpdateUIManager::Instance().updateToolBars();
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::handleMenuActionTrigger(QAction* pAction)
|
|
{
|
|
const QMenu* pMenu = qobject_cast<const QMenu*>(sender());
|
|
if (!pMenu)
|
|
return;
|
|
|
|
//now execute the button
|
|
if (RobloxDocManager::Instance().getCurrentDoc())
|
|
{
|
|
if (pMenu->property("useActionName").toBool())
|
|
{
|
|
RobloxDocManager::Instance().getCurrentDoc()->handleAction(pAction->objectName(), pAction->isChecked());
|
|
UpdateUIManager::Instance().updateToolBars();
|
|
}
|
|
else
|
|
{
|
|
populateNameValueStore(pMenu->objectName(), pAction);
|
|
RobloxDocManager::Instance().getCurrentDoc()->handleAction(pMenu->objectName(), pAction->isChecked());
|
|
}
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::handleBasicObjectsGalleryItemClicked(RibbonGalleryItem* item)
|
|
{
|
|
Qtitan::RibbonGallery* pGallery = qobject_cast<Qtitan::RibbonGallery*>(sender());
|
|
if (!pGallery)
|
|
return;
|
|
|
|
pGallery->setCheckedItem(item);
|
|
|
|
//now execute the action
|
|
if (RobloxDocManager::Instance().getCurrentDoc())
|
|
{
|
|
NameValueStoreManager::singleton().setValue(pGallery->objectName(), "user_value", item->data(100));
|
|
RobloxDocManager::Instance().getCurrentDoc()->handleAction(pGallery->objectName(), false);
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::handleCheckBoxClicked(bool state)
|
|
{
|
|
const QCheckBox* pCheckBox = qobject_cast<const QCheckBox*>(sender());
|
|
if (!pCheckBox)
|
|
return;
|
|
|
|
if (RobloxDocManager::Instance().getCurrentDoc())
|
|
{
|
|
NameValueStoreManager::singleton().setValue(pCheckBox->objectName(), "enabled", pCheckBox->isEnabled());
|
|
NameValueStoreManager::singleton().setValue(pCheckBox->objectName(), "checked", pCheckBox->isChecked());
|
|
RobloxDocManager::Instance().getCurrentDoc()->handleAction(pCheckBox->objectName(), false);
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::handleComboBoxActivated(int index)
|
|
{
|
|
const QComboBox* pComboBox = qobject_cast<const QComboBox*>(sender());
|
|
if (!pComboBox)
|
|
return;
|
|
|
|
if (RobloxDocManager::Instance().getCurrentDoc())
|
|
{
|
|
if (index < 0)
|
|
{
|
|
NameValueStoreManager::singleton().clear(pComboBox->objectName());
|
|
}
|
|
else
|
|
{
|
|
NameValueStoreManager::singleton().setValue(pComboBox->objectName(), "index", index);
|
|
NameValueStoreManager::singleton().setValue(pComboBox->objectName(), "user_value", pComboBox->itemData(index));
|
|
}
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::handleItemSelected(const QString& selectedItem)
|
|
{
|
|
QToolButton* item = qobject_cast<QToolButton*>(sender());
|
|
|
|
if (!item || !item->defaultAction())
|
|
return;
|
|
|
|
// Now execute the action.
|
|
if (RobloxDocManager::Instance().getCurrentDoc())
|
|
{
|
|
NameValueStoreManager::singleton().setValue(item->objectName(), "user_value", selectedItem);
|
|
item->defaultAction()->setProperty("selectedItem", selectedItem);
|
|
RobloxDocManager::Instance().getCurrentDoc()->handleAction(item->objectName(), false);
|
|
UpdateUIManager::Instance().updateToolBars();
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::handleToolButtonClicked()
|
|
{
|
|
QToolButton* item = qobject_cast<QToolButton*>(sender());
|
|
|
|
if (!item || !item->defaultAction())
|
|
return;
|
|
|
|
handleItemSelected(item->defaultAction()->property("selectedItem").toString());
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::handleColorPickMenu()
|
|
{
|
|
QMenu* pMenu = qobject_cast<QMenu*>(sender());
|
|
QToolButton* pButton = pMenu ? qobject_cast<QToolButton*>(pMenu->parent()) : NULL;
|
|
if (!pMenu || !pButton)
|
|
return;
|
|
|
|
// create colorpicker frame and it show at appropriate location
|
|
ColorPickerFrame *pPickerFrame = new ColorPickerFrame(this);
|
|
QVariant var = pButton->defaultAction()->property("selectedIndex");
|
|
if (!var.isNull() && var.isValid())
|
|
pPickerFrame->setSelectedIndex(var.toInt());
|
|
pPickerFrame->move(pButton->mapToGlobal(pButton->rect().bottomLeft()));
|
|
pPickerFrame->show();
|
|
|
|
// create an event loop
|
|
QEventLoop* eventLoop = new QEventLoop(pPickerFrame);
|
|
pPickerFrame->setEventLoop(eventLoop);
|
|
eventLoop->exec();
|
|
|
|
// get selected color
|
|
int selectedColor = pPickerFrame->getSelectedItem();
|
|
|
|
// let document handle the selection
|
|
if (selectedColor != -1 && RobloxDocManager::Instance().getCurrentDoc())
|
|
{
|
|
NameValueStoreManager::singleton().setValue(pButton->objectName(), "user_value", selectedColor);
|
|
pButton->defaultAction()->setProperty("selectedItem", selectedColor);
|
|
pButton->defaultAction()->setProperty("selectedIndex", pPickerFrame->getSelectedIndex());
|
|
RobloxDocManager::Instance().getCurrentDoc()->handleAction(pButton->objectName(), false);
|
|
UpdateUIManager::Instance().updateToolBars();
|
|
}
|
|
|
|
// cleanup
|
|
delete eventLoop;
|
|
delete pPickerFrame;
|
|
pMenu->hide();
|
|
pButton->setDown(false);
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::handleRibbonMinimizationChanged(bool minimized)
|
|
{
|
|
m_pRibbonMinimizeAction->setChecked(minimized);
|
|
|
|
m_pRibbonMinimizeAction->setIcon(minimized ? QIcon(":/RibbonBar/images/Studio Ribbon Icons/ribbonMaximize.png")
|
|
: QIcon(":/RibbonBar/images/Studio Ribbon Icons/ribbonMinimize.png"));
|
|
|
|
RobloxSettings().setValue(settingRibbonMinimized, minimized);
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::toggleRibbonMinimize()
|
|
{
|
|
ribbonBar()->setMinimized(!ribbonBar()->isMinimized());
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::handleStyleChange(QAction* pAction)
|
|
{
|
|
if (!m_pRibbonStyle)
|
|
return;
|
|
|
|
Qtitan::RibbonStyle::Theme themeId = Qtitan::RibbonStyle::Office2010Black;
|
|
if (pAction->objectName() == "Office2007Blue")
|
|
themeId = Qtitan::RibbonStyle::Office2007Blue;
|
|
else if (pAction->objectName() == "Office2007Black")
|
|
themeId = Qtitan::RibbonStyle::Office2007Black;
|
|
else if (pAction->objectName() == "Office2007Silver")
|
|
themeId = Qtitan::RibbonStyle::Office2007Silver;
|
|
else if (pAction->objectName() == "Office2007Aqua")
|
|
themeId = Qtitan::RibbonStyle::Office2007Aqua;
|
|
else if (pAction->objectName() == "Windows7Scenic")
|
|
themeId = Qtitan::OfficeStyle::Windows7Scenic;
|
|
else if (pAction->objectName() == "Office2010Blue")
|
|
themeId = Qtitan::OfficeStyle::Office2010Blue;
|
|
else if (pAction->objectName() == "Office2010Silver")
|
|
themeId = Qtitan::OfficeStyle::Office2010Silver;
|
|
else if (pAction->objectName() == "Office2010Black")
|
|
themeId = Qtitan::OfficeStyle::Office2010Black;
|
|
else if (pAction->objectName() == "Office2013White")
|
|
themeId = Qtitan::OfficeStyle::Office2013White;
|
|
else if (pAction->objectName() == "Office2013Gray")
|
|
themeId = Qtitan::OfficeStyle::Office2013Gray;
|
|
|
|
setTheme(themeId);
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::initialize()
|
|
{
|
|
cleanupToolBars();
|
|
|
|
// Add all previously created actions to the ribbon so we have accelerators (even though they may not be visible)
|
|
QList<QAction*> childActions = this->findChildren<QAction*>();
|
|
for (int i = 0; i < childActions.length(); i++)
|
|
this->addAction(childActions.at(i));
|
|
|
|
// we do no want mainwindow to steal focus
|
|
setFocusPolicy(Qt::NoFocus);
|
|
|
|
QByteArray fontData = QtUtilities::getResourceFileBytes(":/fonts/SourceSansPro/TTF/SourceSansPro-Regular.ttf");
|
|
int fontIndex = QFontDatabase::addApplicationFontFromData(fontData);
|
|
|
|
QStringList loadedFontFamilies = QFontDatabase::applicationFontFamilies(fontIndex);
|
|
RBXASSERT(!loadedFontFamilies.empty());
|
|
|
|
m_ribbonFont = QFont(loadedFontFamilies.at(0), 9);
|
|
|
|
#ifdef Q_WS_MAC
|
|
// On Mac we need to have bigger fonts
|
|
m_ribbonFont.setPointSize(m_ribbonFont.pointSize() + 4);
|
|
|
|
QApplication::setFont(m_ribbonFont);
|
|
QApplication::setFont(m_ribbonFont, "QListWidget");
|
|
#else
|
|
QApplication::setFont(m_ribbonFont);
|
|
#endif
|
|
|
|
// create default style
|
|
m_pRibbonStyle = new Qtitan::RibbonStyle();
|
|
m_pRibbonStyle->setTheme(Qtitan::OfficeStyle::Office2013White);
|
|
m_pRibbonStyle->setAnimationEnabled(false);
|
|
qApp->setStyle(m_pRibbonStyle);
|
|
|
|
// ribbon bar object (MUST)
|
|
setRibbonBar(new RibbonBar(this));
|
|
|
|
ribbonBar()->setFont(m_ribbonFont);
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::cleanupToolBars()
|
|
{
|
|
QList<QToolBar*> toolbars = findChildren<QToolBar*>();
|
|
for (int ii = 0; ii < toolbars.count(); ++ii)
|
|
{
|
|
if (toolbars.at(ii) != commandToolBar)
|
|
{
|
|
removeToolBar(toolbars.at(ii));
|
|
delete toolbars.at(ii);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::updateFonts(const QFont& font)
|
|
{
|
|
if (font == m_ribbonFont)
|
|
{
|
|
QFont smallerFont(font);
|
|
smallerFont.setPointSize(font.pointSize() - 1);
|
|
|
|
const QList<QWidget*> ribbonWidgets = ribbonBar()->findChildren<QWidget*>();
|
|
for (int ii = 0; ii < ribbonWidgets.count(); ii++)
|
|
{
|
|
if (ribbonWidgets[ii] && ribbonWidgets[ii]->metaObject())
|
|
{
|
|
#ifdef Q_WS_MAC
|
|
// Mac requires setting of fonts to both i.e. Tab as well as other children
|
|
ribbonWidgets[ii]->setFont(ribbonWidgets[ii]->metaObject()->className() == QString("Qtitan::RibbonTab") ? smallerFont : font);
|
|
#else
|
|
if (ribbonWidgets[ii]->metaObject()->className() == QString("Qtitan::RibbonTab"))
|
|
ribbonWidgets[ii]->setFont(smallerFont);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
RibbonSystemButton* pSystemButton = ribbonBar()->getSystemButton();
|
|
if (pSystemButton && pSystemButton->defaultAction() && pSystemButton->defaultAction()->menu())
|
|
{
|
|
pSystemButton->defaultAction()->menu()->setFont(font);
|
|
const QList<QAction*>& systemMenuActions = pSystemButton->defaultAction()->menu()->actions();
|
|
for (int ii = 0; ii < systemMenuActions.count(); ii++)
|
|
{
|
|
if (systemMenuActions[ii])
|
|
systemMenuActions[ii]->setFont(font);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QApplication::setFont(font);
|
|
// on mac we need explicit font update
|
|
#ifdef Q_WS_MAC
|
|
QApplication::setFont(font, "QListWidget");
|
|
#endif
|
|
|
|
QFont smallerFont(font);
|
|
smallerFont.setPointSize(font.pointSize() - 1);
|
|
|
|
ribbonBar()->setFont(font);
|
|
const QList<QWidget*> ribbonWidgets = ribbonBar()->findChildren<QWidget*>();
|
|
for (int ii = 0; ii < ribbonWidgets.count(); ii++)
|
|
{
|
|
if (ribbonWidgets[ii] && ribbonWidgets[ii]->metaObject())
|
|
ribbonWidgets[ii]->setFont(ribbonWidgets[ii]->metaObject()->className() == QString("Qtitan::RibbonTab") ? smallerFont : font);
|
|
}
|
|
|
|
RibbonSystemButton* pSystemButton = ribbonBar()->getSystemButton();
|
|
if (pSystemButton && pSystemButton->defaultAction() && pSystemButton->defaultAction()->menu())
|
|
{
|
|
pSystemButton->defaultAction()->menu()->setFont(font);
|
|
const QList<QAction*>& systemMenuActions = pSystemButton->defaultAction()->menu()->actions();
|
|
for (int ii = 0; ii < systemMenuActions.count(); ii++)
|
|
{
|
|
if (systemMenuActions[ii])
|
|
systemMenuActions[ii]->setFont(font);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::customizeQuickAccess()
|
|
{
|
|
if (!m_pQuickAccessConfigDialog)
|
|
m_pQuickAccessConfigDialog = new QuickAccessConfigDialog(this);
|
|
|
|
// toggle visibility
|
|
if (m_pQuickAccessConfigDialog->isVisible())
|
|
{
|
|
m_pQuickAccessConfigDialog->hide();
|
|
}
|
|
else
|
|
{
|
|
m_pQuickAccessConfigDialog->show();
|
|
m_pQuickAccessConfigDialog->raise();
|
|
}
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::createThemeOptions()
|
|
{
|
|
Qtitan::RibbonStyle::Theme themeId = Qtitan::RibbonStyle::Office2007Blue;
|
|
if (m_pRibbonStyle)
|
|
themeId = m_pRibbonStyle->getTheme();
|
|
|
|
QMenu* menu = ribbonBar()->addMenu(tr("Theme Options"));
|
|
QAction* actionStyle = menu->addAction(tr(attribStyle));
|
|
|
|
QMenu* menuStyle = new QMenu(ribbonBar());
|
|
QActionGroup* styleActions = new QActionGroup(this);
|
|
|
|
QAction* actionBlue = menuStyle->addAction(tr("Office 2007 Blue"));
|
|
actionBlue->setObjectName("Office2007Blue");
|
|
actionBlue->setChecked(themeId == Qtitan::RibbonStyle::Office2007Blue);
|
|
|
|
QAction* actionBlack = menuStyle->addAction(tr("Office 2007 Black"));
|
|
actionBlack->setObjectName("Office2007Black");
|
|
actionBlack->setChecked(themeId == Qtitan::RibbonStyle::Office2007Black);
|
|
|
|
QAction* actionSilver = menuStyle->addAction(tr("Office 2007 Silver"));
|
|
actionSilver->setObjectName("Office2007Silver");
|
|
actionSilver->setChecked(themeId == Qtitan::RibbonStyle::Office2007Silver);
|
|
|
|
QAction* actionAqua = menuStyle->addAction(tr("Office 2007 Aqua"));
|
|
actionAqua->setObjectName("Office2007Aqua");
|
|
actionAqua->setChecked(themeId == Qtitan::RibbonStyle::Office2007Aqua);
|
|
|
|
QAction* actionScenic = menuStyle->addAction(tr("Windows 7 Scenic"));
|
|
actionScenic->setObjectName("Windows7Scenic");
|
|
actionScenic->setChecked(themeId == Qtitan::RibbonStyle::Windows7Scenic);
|
|
|
|
QAction* action2010Blue = menuStyle->addAction(tr("Office 2010 Blue"));
|
|
action2010Blue->setObjectName("Office2010Blue");
|
|
action2010Blue->setChecked(themeId == Qtitan::RibbonStyle::Office2010Blue);
|
|
|
|
QAction* action2010Silver = menuStyle->addAction(tr("Office 2010 Silver"));
|
|
action2010Silver->setObjectName("Office2010Silver");
|
|
action2010Silver->setChecked(themeId == Qtitan::RibbonStyle::Office2010Silver);
|
|
|
|
QAction* action2010Black = menuStyle->addAction(tr("Office 2010 Black"));
|
|
action2010Black->setObjectName("Office2010Black");
|
|
action2010Black->setChecked(themeId == Qtitan::RibbonStyle::Office2010Black);
|
|
|
|
QAction* action2013White = menuStyle->addAction(tr("Office 2013 White"));
|
|
action2013White->setObjectName("Office2013White");
|
|
action2013White->setChecked(themeId == Qtitan::RibbonStyle::Office2013White);
|
|
|
|
QAction* action2013Gray = menuStyle->addAction(tr("Office 2013 Gray"));
|
|
action2013Gray->setObjectName("Office2013Gray");
|
|
action2013Gray->setChecked(themeId == Qtitan::RibbonStyle::Office2013Gray);
|
|
|
|
styleActions->addAction(actionBlue);
|
|
styleActions->addAction(actionBlack);
|
|
styleActions->addAction(actionSilver);
|
|
styleActions->addAction(actionAqua);
|
|
styleActions->addAction(actionScenic);
|
|
styleActions->addAction(action2010Blue);
|
|
styleActions->addAction(action2010Silver);
|
|
styleActions->addAction(action2010Black);
|
|
styleActions->addAction(action2013White);
|
|
styleActions->addAction(action2013Gray);
|
|
styleActions->setEnabled(true);
|
|
|
|
QList<QAction*> styleActionsList= styleActions->actions();
|
|
for(int i = 0 ; i < styleActionsList.size(); i++)
|
|
{
|
|
styleActionsList.at(i)->setCheckable(true);
|
|
}
|
|
|
|
actionStyle->setMenu(menuStyle);
|
|
connect(styleActions, SIGNAL(triggered(QAction*)), this, SLOT(handleStyleChange(QAction*)));
|
|
}
|
|
|
|
void RobloxRibbonMainWindow::setTheme(Qtitan::RibbonStyle::Theme themeId)
|
|
{
|
|
if (!m_pRibbonStyle)
|
|
return;
|
|
|
|
if (QToolButton* pButton = ribbonBar()->getSystemButton())
|
|
{
|
|
if (themeId == Qtitan::OfficeStyle::Windows7Scenic ||
|
|
themeId == Qtitan::OfficeStyle::Office2010Blue ||
|
|
themeId == Qtitan::OfficeStyle::Office2010Silver ||
|
|
themeId == Qtitan::OfficeStyle::Office2010Black ||
|
|
themeId == Qtitan::OfficeStyle::Office2013White ||
|
|
themeId == Qtitan::OfficeStyle::Office2013Gray)
|
|
{
|
|
pButton->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
|
}
|
|
else
|
|
{
|
|
pButton->setToolButtonStyle(Qt::ToolButtonFollowStyle);
|
|
}
|
|
}
|
|
|
|
m_pRibbonStyle->setTheme(themeId);
|
|
}
|
|
|
|
QAction* RobloxRibbonMainWindow::findChildAction(const QString& actionName) const
|
|
{ return findChild<QAction*>(actionName); }
|
|
|
|
void RobloxRibbonMainWindow::populateNameValueStore(const QString& storeID, const QObject *pObject)
|
|
{
|
|
//first clear the store
|
|
NameValueStoreManager::singleton().clear(storeID);
|
|
|
|
if (!pObject || !pObject->dynamicPropertyNames().count())
|
|
return;
|
|
|
|
//now we are ready to populate
|
|
NameValueStoreManager::singleton().setValue(storeID, "objectName", pObject->objectName());
|
|
//set the dynamic properties for later use
|
|
for (int ii = 0; ii < pObject->dynamicPropertyNames().count(); ++ii)
|
|
{
|
|
QString propName = pObject->dynamicPropertyNames().at(ii);
|
|
if (!propName.isEmpty())
|
|
NameValueStoreManager::singleton().setValue(storeID, propName, pObject->property(qPrintable(propName)));
|
|
}
|
|
}
|
|
|
|
QString RobloxRibbonMainWindow::getFullName(QObject* pObject)
|
|
{
|
|
QString fullName;
|
|
while (pObject && (pObject != this))
|
|
{
|
|
if (!fullName.isEmpty())
|
|
fullName.append('.');
|
|
fullName.append(pObject->objectName());
|
|
pObject = pObject->parent();
|
|
}
|
|
return fullName;
|
|
} |