From 71ce78f949b8237da03341808bdaaf1b1871e51a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 31 Jul 2014 13:05:08 +0200 Subject: [PATCH] added scene toolbar toggle widget --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/widget/pushbutton.cpp | 15 +- apps/opencs/view/widget/pushbutton.hpp | 3 +- apps/opencs/view/widget/scenetooltoggle.cpp | 204 ++++++++++++++++++++ apps/opencs/view/widget/scenetooltoggle.hpp | 75 +++++++ 5 files changed, 295 insertions(+), 4 deletions(-) create mode 100644 apps/opencs/view/widget/scenetooltoggle.cpp create mode 100644 apps/opencs/view/widget/scenetooltoggle.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index e1e467889..342e3f2c5 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -69,7 +69,7 @@ opencs_units_noqt (view/world ) opencs_units (view/widget - scenetoolbar scenetool scenetoolmode pushbutton + scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle ) opencs_units (view/render diff --git a/apps/opencs/view/widget/pushbutton.cpp b/apps/opencs/view/widget/pushbutton.cpp index 056e548fb..ccf89e18a 100644 --- a/apps/opencs/view/widget/pushbutton.cpp +++ b/apps/opencs/view/widget/pushbutton.cpp @@ -26,6 +26,14 @@ void CSVWidget::PushButton::setExtendedToolTip (const QString& text) "

(left click to activate," "
shift-left click to activate and keep panel open)"; + case Type_Toggle: + + tooltip += "

(left click to "; + tooltip += isChecked() ? "disable" : "enable"; + tooltip += "

shift-left click to "; + tooltip += isChecked() ? "disable" : "enable"; + tooltip += " and keep panel open)"; + break; } @@ -45,6 +53,9 @@ void CSVWidget::PushButton::keyReleaseEvent (QKeyEvent *event) if (event->key()==Qt::Key_Return || event->key()==Qt::Key_Enter) { mKeepOpen = event->modifiers() & Qt::ShiftModifier; + + setChecked (!isChecked()); + emit clicked(); } @@ -61,14 +72,14 @@ CSVWidget::PushButton::PushButton (const QIcon& icon, Type type, const QString& QWidget *parent) : QPushButton (icon, "", parent), mKeepOpen (false), mType (type), mToolTip (tooltip) { - setCheckable (type==Type_Mode); + setCheckable (type==Type_Mode || type==Type_Toggle); setExtendedToolTip (tooltip); } CSVWidget::PushButton::PushButton (Type type, const QString& tooltip, QWidget *parent) : QPushButton (parent), mKeepOpen (false), mType (type), mToolTip (tooltip) { - setCheckable (type==Type_Mode); + setCheckable (type==Type_Mode || type==Type_Toggle); setExtendedToolTip (tooltip); } diff --git a/apps/opencs/view/widget/pushbutton.hpp b/apps/opencs/view/widget/pushbutton.hpp index 9b90bd0c6..eae0f0a24 100644 --- a/apps/opencs/view/widget/pushbutton.hpp +++ b/apps/opencs/view/widget/pushbutton.hpp @@ -14,7 +14,8 @@ namespace CSVWidget enum Type { Type_TopMode, // top level button for mode selector panel - Type_Mode // mode button + Type_Mode, // mode button + Type_Toggle }; private: diff --git a/apps/opencs/view/widget/scenetooltoggle.cpp b/apps/opencs/view/widget/scenetooltoggle.cpp new file mode 100644 index 000000000..1737bbfc7 --- /dev/null +++ b/apps/opencs/view/widget/scenetooltoggle.cpp @@ -0,0 +1,204 @@ + +#include "scenetooltoggle.hpp" + +#include + +#include +#include +#include +#include + +#include "scenetoolbar.hpp" +#include "pushbutton.hpp" + +void CSVWidget::SceneToolToggle::adjustToolTip() +{ + QString toolTip = mToolTip; + + toolTip += "

Currently enabled: "; + + bool first = true; + + for (std::map::const_iterator iter (mButtons.begin()); + iter!=mButtons.end(); ++iter) + if (iter->first->isChecked()) + { + if (!first) + toolTip += ", "; + else + first = false; + + toolTip += iter->second.mName; + } + + if (first) + toolTip += "none"; + + toolTip += "

(left click to alter selection)"; + + setToolTip (toolTip); +} + +void CSVWidget::SceneToolToggle::adjustIcon() +{ + unsigned int selection = getSelection(); + if (!selection) + setIcon (QIcon (QString::fromUtf8 (mEmptyIcon.c_str()))); + else + { + QPixmap pixmap (48, 48); + pixmap.fill (QColor (0, 0, 0, 0)); + + { + QPainter painter (&pixmap); + + for (std::map::const_iterator iter (mButtons.begin()); + iter!=mButtons.end(); ++iter) + if (iter->first->isChecked()) + { + painter.drawImage (getIconBox (iter->second.mIndex), + QImage (QString::fromUtf8 (iter->second.mSmallIcon.c_str()))); + } + } + + setIcon (pixmap); + } +} + +QRect CSVWidget::SceneToolToggle::getIconBox (int index) const +{ + // layout for a 3x3 grid + int xMax = 3; + int yMax = 3; + + // icon size + int xBorder = 1; + int yBorder = 1; + + int iconXSize = (mIconSize-xBorder*(xMax+1))/xMax; + int iconYSize = (mIconSize-yBorder*(yMax+1))/yMax; + + int y = index / xMax; + int x = index % xMax; + + int total = mButtons.size(); + + int actualYIcons = total/xMax; + + if (total % xMax) + ++actualYIcons; + + if (actualYIcons!=yMax) + { + // space out icons vertically, if there aren't enough to populate all rows + int diff = yMax - actualYIcons; + yBorder += (diff*(yBorder+iconXSize)) / actualYIcons; + } + + if (y==actualYIcons-1) + { + // generating the last row of icons + int actualXIcons = total % xMax; + + if (actualYIcons) + { + // space out icons horizontally, if there aren't enough to fill the last row + int diff = xMax - actualYIcons; + + xBorder += (diff*(xBorder+iconYSize)) / actualXIcons; + } + } + + return QRect ((iconXSize+xBorder)*x+xBorder, (iconYSize+yBorder)*y+yBorder, + iconXSize, iconYSize); +} + +CSVWidget::SceneToolToggle::SceneToolToggle (SceneToolbar *parent, const QString& toolTip, + const std::string& emptyIcon) +: SceneTool (parent), mEmptyIcon (emptyIcon), mButtonSize (parent->getButtonSize()), + mIconSize (parent->getIconSize()), mToolTip (toolTip), mFirst (0) +{ + mPanel = new QFrame (this, Qt::Popup); + + mLayout = new QHBoxLayout (mPanel); + + mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); + + mPanel->setLayout (mLayout); +} + +void CSVWidget::SceneToolToggle::showPanel (const QPoint& position) +{ + mPanel->move (position); + mPanel->show(); + + if (mFirst) + mFirst->setFocus (Qt::OtherFocusReason); +} + +void CSVWidget::SceneToolToggle::addButton (const std::string& icon, unsigned int id, + const std::string& smallIcon, const QString& name, const QString& tooltip) +{ + if (mButtons.size()>=9) + throw std::runtime_error ("Exceeded number of buttons in toggle type tool"); + + PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), + PushButton::Type_Toggle, tooltip, mPanel); + button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); + button->setIconSize (QSize (mIconSize, mIconSize)); + button->setFixedSize (mButtonSize, mButtonSize); + + mLayout->addWidget (button); + + ButtonDesc desc; + desc.mId = id; + desc.mSmallIcon = smallIcon; + desc.mName = name; + desc.mIndex = mButtons.size(); + + mButtons.insert (std::make_pair (button, desc)); + + connect (button, SIGNAL (clicked()), this, SLOT (selected())); + + if (mButtons.size()==1) + mFirst = button; +} + +unsigned int CSVWidget::SceneToolToggle::getSelection() const +{ + unsigned int selection = 0; + + for (std::map::const_iterator iter (mButtons.begin()); + iter!=mButtons.end(); ++iter) + if (iter->first->isChecked()) + selection |= iter->second.mId; + + return selection; +} + +void CSVWidget::SceneToolToggle::setSelection (unsigned int selection) +{ + for (std::map::iterator iter (mButtons.begin()); + iter!=mButtons.end(); ++iter) + iter->first->setChecked (selection & iter->second.mId); + + adjustToolTip(); + adjustIcon(); +} + +void CSVWidget::SceneToolToggle::selected() +{ + std::map::const_iterator iter = + mButtons.find (dynamic_cast (sender())); + + if (iter!=mButtons.end()) + { + if (!iter->first->hasKeepOpen()) + mPanel->hide(); + + adjustToolTip(); + adjustIcon(); + + emit selectionChanged(); + } +} diff --git a/apps/opencs/view/widget/scenetooltoggle.hpp b/apps/opencs/view/widget/scenetooltoggle.hpp new file mode 100644 index 000000000..55e697524 --- /dev/null +++ b/apps/opencs/view/widget/scenetooltoggle.hpp @@ -0,0 +1,75 @@ +#ifndef CSV_WIDGET_SCENETOOL_TOGGLE_H +#define CSV_WIDGET_SCENETOOL_TOGGLE_H + +#include "scenetool.hpp" + +#include + +class QHBoxLayout; +class QRect; + +namespace CSVWidget +{ + class SceneToolbar; + class PushButton; + + ///< \brief Multi-Toggle tool + class SceneToolToggle : public SceneTool + { + Q_OBJECT + + struct ButtonDesc + { + unsigned int mId; + std::string mSmallIcon; + QString mName; + int mIndex; + }; + + std::string mEmptyIcon; + QWidget *mPanel; + QHBoxLayout *mLayout; + std::map mButtons; // widget, id + int mButtonSize; + int mIconSize; + QString mToolTip; + PushButton *mFirst; + + void adjustToolTip(); + + void adjustIcon(); + + QRect getIconBox (int index) const; + + public: + + SceneToolToggle (SceneToolbar *parent, const QString& toolTip, + const std::string& emptyIcon); + + virtual void showPanel (const QPoint& position); + + /// \attention After the last button has been added, setSelection must be called at + /// least once to finalise the layout. + /// + /// \note The layout algorithm can not handle more than 9 buttons. To prevent this An + /// attempt to add more will result in an exception being thrown. + /// The small icons will be sized at (x-4)/3 (where x is the main icon size). + void addButton (const std::string& icon, unsigned int id, + const std::string& smallIcon, const QString& name, const QString& tooltip = ""); + + unsigned int getSelection() const; + + /// \param or'ed button IDs. IDs that do not exist will be ignored. + void setSelection (unsigned int selection); + + signals: + + void selectionChanged(); + + private slots: + + void selected(); + }; +} + +#endif