diff --git a/libs/widgets/KoToolBox.cpp b/libs/widgets/KoToolBox.cpp index b614edc21c..74cad37b72 100644 --- a/libs/widgets/KoToolBox.cpp +++ b/libs/widgets/KoToolBox.cpp @@ -1,372 +1,372 @@ /* * Copyright (c) 2005-2009 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoToolBox_p.h" #include "KoToolBoxLayout_p.h" #include "KoToolBoxButton_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BUTTON_MARGIN 10 static int buttonSize(int screen) { QRect rc = qApp->desktop()->screenGeometry(screen); if (rc.width() <= 1024) { return 12; } else if (rc.width() <= 1377) { return 14; } else if (rc.width() <= 1920 ) { return 16; } else { return 22; } } class KoToolBox::Private { public: Private() : layout(0) , buttonGroup(0) , floating(false) , contextSize(0) { } void addSection(Section *section, const QString &name); QList buttons; QMap sections; KoToolBoxLayout *layout; QButtonGroup *buttonGroup; QHash visibilityCodes; bool floating; QMap contextIconSizes; QMenu* contextSize; Qt::Orientation orientation; }; void KoToolBox::Private::addSection(Section *section, const QString &name) { section->setName(name); layout->addSection(section); sections.insert(name, section); } KoToolBox::KoToolBox() : d(new Private) { d->layout = new KoToolBoxLayout(this); // add defaults d->addSection(new Section(this), "main"); d->addSection(new Section(this), "dynamic"); d->buttonGroup = new QButtonGroup(this); setLayout(d->layout); Q_FOREACH (KoToolAction *toolAction, KoToolManager::instance()->toolActionList()) { addButton(toolAction); } // Update visibility of buttons setButtonsVisible(QList()); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*, int)), this, SLOT(setActiveTool(KoCanvasController*, int))); connect(KoToolManager::instance(), SIGNAL(currentLayerChanged(const KoCanvasController*,const KoShapeLayer*)), this, SLOT(setCurrentLayer(const KoCanvasController*,const KoShapeLayer*))); connect(KoToolManager::instance(), SIGNAL(toolCodesSelected(QList)), this, SLOT(setButtonsVisible(QList))); connect(KoToolManager::instance(), SIGNAL(addedTool(KoToolAction*,KoCanvasController*)), this, SLOT(toolAdded(KoToolAction*,KoCanvasController*))); QTimer::singleShot(0, this, SLOT(adjustToFit())); } KoToolBox::~KoToolBox() { delete d; } void KoToolBox::addButton(KoToolAction *toolAction) { KoToolBoxButton *button = new KoToolBoxButton(toolAction, this); d->buttons << button; int toolbuttonSize = buttonSize(qApp->desktop()->screenNumber(this)); KConfigGroup cfg = KSharedConfig::openConfig()->group("KoToolBox"); int iconSize = cfg.readEntry("iconSize", toolbuttonSize); button->setIconSize(QSize(iconSize, iconSize)); foreach (Section *section, d->sections.values()) { section->setButtonSize(QSize(iconSize + BUTTON_MARGIN, iconSize + BUTTON_MARGIN)); } QString sectionToBeAddedTo; const QString section = toolAction->section(); if (section.contains(qApp->applicationName())) { sectionToBeAddedTo = "main"; } else if (section.contains("main")) { sectionToBeAddedTo = "main"; } else if (section.contains("dynamic")) { sectionToBeAddedTo = "dynamic"; } else { sectionToBeAddedTo = section; } Section *sectionWidget = d->sections.value(sectionToBeAddedTo); if (sectionWidget == 0) { sectionWidget = new Section(this); d->addSection(sectionWidget, sectionToBeAddedTo); } sectionWidget->addButton(button, toolAction->priority()); d->buttonGroup->addButton(button, toolAction->buttonGroupId()); d->visibilityCodes.insert(button, toolAction->visibilityCode()); } void KoToolBox::setActiveTool(KoCanvasController *canvas, int id) { Q_UNUSED(canvas); QAbstractButton *button = d->buttonGroup->button(id); if (button) { button->setChecked(true); (qobject_cast(button))->setHighlightColor(); } else { warnWidgets << "KoToolBox::setActiveTool(" << id << "): no such button found"; } } void KoToolBox::setButtonsVisible(const QList &codes) { Q_FOREACH (QToolButton *button, d->visibilityCodes.keys()) { QString code = d->visibilityCodes.value(button); if (code.startsWith(QLatin1String("flake/"))) { continue; } if (code.endsWith( QLatin1String( "/always"))) { button->setVisible(true); - button->setEnabled( true ); + button->setEnabled(true); } else if (code.isEmpty()) { button->setVisible(true); button->setEnabled( codes.count() != 0 ); } else { button->setVisible( codes.contains(code) ); } } layout()->invalidate(); update(); } void KoToolBox::setCurrentLayer(const KoCanvasController *canvas, const KoShapeLayer *layer) { Q_UNUSED(canvas); const bool enabled = layer == 0 || (layer->isEditable() && layer->isVisible()); foreach (QToolButton *button, d->visibilityCodes.keys()) { if (d->visibilityCodes[button].endsWith( QLatin1String( "/always") ) ) { continue; } button->setEnabled(enabled); } } void KoToolBox::paintEvent(QPaintEvent *) { QPainter painter(this); const QList sections = d->sections.values(); QList::const_iterator iterator = sections.begin(); int halfSpacing = layout()->spacing(); if (halfSpacing > 0) { halfSpacing /= 2; } while(iterator != sections.end()) { Section *section = *iterator; QStyleOption styleoption; styleoption.palette = palette(); if (section->separators() & Section::SeparatorTop) { int y = section->y() - halfSpacing; styleoption.state = QStyle::State_None; styleoption.rect = QRect(section->x(), y-1, section->width(), 2); style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &styleoption, &painter); } if (section->separators() & Section::SeparatorLeft) { int x = section->x() - halfSpacing; styleoption.state = QStyle::State_Horizontal; styleoption.rect = QRect(x-1, section->y(), 2, section->height()); style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &styleoption, &painter); } ++iterator; } painter.end(); } void KoToolBox::resizeEvent(QResizeEvent* event) { QRect availableRc = qApp->desktop()->availableGeometry(this); QSize layoutSize = layout()->minimumSize(); QWidget::resizeEvent(event); if (layoutSize.height() > availableRc.height()) { setMinimumWidth(layout()->minimumSize().width() * 2); // This enforces the minimum size on the widget adjustToFit(); } else if (layoutSize.width() > availableRc.width()) { setMinimumHeight(layout()->minimumSize().height() * 2); // This enforces the minimum size on the widget adjustToFit(); } } void KoToolBox::setOrientation(Qt::Orientation orientation) { d->orientation = orientation; d->layout->setOrientation(orientation); QTimer::singleShot(0, this, SLOT(update())); Q_FOREACH (Section* section, d->sections) { section->setOrientation(orientation); } } void KoToolBox::setFloating(bool v) { setMinimumSize(QSize(1,1)); d->floating = v; } void KoToolBox::toolAdded(KoToolAction *toolAction, KoCanvasController *canvas) { Q_UNUSED(canvas); addButton(toolAction); setButtonsVisible(QList()); } void KoToolBox::adjustToFit() { int newWidth = width() - (width() % layout()->minimumSize().width()); if (newWidth != width() && newWidth >= layout()->minimumSize().width()) { setMaximumWidth(newWidth); QTimer::singleShot(0, this, SLOT(resizeUnlock())); } } void KoToolBox::resizeUnlock() { setMaximumWidth(QWIDGETSIZE_MAX); } void KoToolBox::slotContextIconSize() { QAction* action = qobject_cast(sender()); if (action && d->contextIconSizes.contains(action)) { const int iconSize = d->contextIconSizes.value(action); KConfigGroup cfg = KSharedConfig::openConfig()->group("KoToolBox"); cfg.writeEntry("iconSize", iconSize); Q_FOREACH (QToolButton *button, d->buttons) { button->setIconSize(QSize(iconSize, iconSize)); } Q_FOREACH (Section *section, d->sections.values()) { section->setButtonSize(QSize(iconSize + BUTTON_MARGIN, iconSize + BUTTON_MARGIN)); } } adjustToFit(); } void KoToolBox::contextMenuEvent(QContextMenuEvent *event) { int toolbuttonSize = buttonSize(qApp->desktop()->screenNumber(this)); if (!d->contextSize) { d->contextSize = new QMenu(i18n("Icon Size"), this); d->contextIconSizes.insert(d->contextSize->addAction(i18nc("@item:inmenu Icon size", "Default"), this, SLOT(slotContextIconSize())), toolbuttonSize); QList sizes; sizes << 12 << 14 << 16 << 22 << 32 << 48 << 64; //<< 96 << 128 << 192 << 256; Q_FOREACH (int i, sizes) { d->contextIconSizes.insert(d->contextSize->addAction(i18n("%1x%2", i, i), this, SLOT(slotContextIconSize())), i); } QActionGroup *sizeGroup = new QActionGroup(d->contextSize); foreach (QAction *action, d->contextSize->actions()) { action->setActionGroup(sizeGroup); action->setCheckable(true); } } KConfigGroup cfg = KSharedConfig::openConfig()->group("KoToolBox"); toolbuttonSize = cfg.readEntry("iconSize", toolbuttonSize); QMapIterator< QAction*, int > it = d->contextIconSizes; while (it.hasNext()) { it.next(); if (it.value() == toolbuttonSize) { it.key()->setChecked(true); break; } } d->contextSize->exec(event->globalPos()); } diff --git a/libs/widgets/KoToolBoxButton_p.h b/libs/widgets/KoToolBoxButton_p.h index a04abfbe22..6685e9cb97 100644 --- a/libs/widgets/KoToolBoxButton_p.h +++ b/libs/widgets/KoToolBoxButton_p.h @@ -1,41 +1,40 @@ /* * Copyright (c) 2015 Friedrich W. H. Kossebau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KO_TOOLBOXBUTTON_H_ #define _KO_TOOLBOXBUTTON_H_ #include class KoToolAction; class KoToolBoxButton : public QToolButton { Q_OBJECT public: explicit KoToolBoxButton(KoToolAction *toolAction, QWidget * parent); void setHighlightColor(); private Q_SLOTS: void setDataFromToolAction(); - private: KoToolAction *m_toolAction; }; #endif // _KO_TOOLBOXBUTTON_H_ diff --git a/libs/widgets/KoToolBoxLayout_p.h b/libs/widgets/KoToolBoxLayout_p.h index 1e76e7c89f..a2b5e1d214 100644 --- a/libs/widgets/KoToolBoxLayout_p.h +++ b/libs/widgets/KoToolBoxLayout_p.h @@ -1,352 +1,401 @@ /* * Copyright (c) 2005-2009 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KO_TOOLBOX_LAYOUT_H_ #define _KO_TOOLBOX_LAYOUT_H_ #include #include #include #include #include #include #include +#include class SectionLayout : public QLayout { public: explicit SectionLayout(QWidget *parent) : QLayout(parent), m_orientation(Qt::Vertical) { } ~SectionLayout() { qDeleteAll( m_items ); m_items.clear(); } void addButton(QAbstractButton *button, int priority) { addChildWidget(button); m_priorities.insert(button, priority); int index = 1; Q_FOREACH (QWidgetItem *item, m_items) { if (m_priorities.value(static_cast(item->widget())) > priority) break; index++; } m_items.insert(index-1, new QWidgetItem(button)); } QSize sizeHint() const { Q_ASSERT(0); return QSize(); } void addItem(QLayoutItem*) { Q_ASSERT(0); } QLayoutItem* itemAt(int i) const { if (m_items.count() <= i) return 0; return m_items.at(i); } QLayoutItem* takeAt(int i) { return m_items.takeAt(i); } int count() const { return m_items.count(); } void setGeometry (const QRect &rect) { int x = 0; int y = 0; const QSize &size = buttonSize(); if (m_orientation == Qt::Vertical) { foreach (QWidgetItem* w, m_items) { if (w->isEmpty()) continue; w->widget()->setGeometry(QRect(x, y, size.width(), size.height())); x += size.width(); if (x + size.width() > rect.width()) { x = 0; y += size.height(); } } } else { foreach (QWidgetItem* w, m_items) { if (w->isEmpty()) continue; w->widget()->setGeometry(QRect(x, y, size.width(), size.height())); y += size.height(); if (y + size.height() > rect.height()) { x += size.width(); y = 0; } } } } void setButtonSize(const QSize size) { m_buttonSize = size; } const QSize &buttonSize() const { return m_buttonSize; } void setOrientation (Qt::Orientation orientation) { m_orientation = orientation; } private: QSize m_buttonSize; QMap m_priorities; QList m_items; Qt::Orientation m_orientation; }; class Section : public QWidget { public: enum SeparatorFlag { SeparatorTop = 0x0001,/* SeparatorBottom = 0x0002, SeparatorRight = 0x0004,*/ SeparatorLeft = 0x0008 }; Q_DECLARE_FLAGS(Separators, SeparatorFlag) explicit Section(QWidget *parent = 0) : QWidget(parent), m_layout(new SectionLayout(this)) { setLayout(m_layout); +// Re-enable this when we need to debug the section layout again. +// setAutoFillBackground(true); +// static int i = 0; +// switch(i) { +// case 0: +// setStyleSheet("background-color:red"); +// break; +// case 1: +// setStyleSheet("background-color:blue"); +// break; +// case 2: +// setStyleSheet("background-color:green"); +// break; +// case 3: +// setStyleSheet("background-color:yellow"); +// break; +// case 4: +// setStyleSheet("background-color:white"); +// break; +// case 5: +// setStyleSheet("background-color:gray"); +// break; +// case 6: +// setStyleSheet("background-color:lime"); +// break; +// case 7: +// setStyleSheet("background-color:silver"); +// break; +// case 8: +// setStyleSheet("background-color:purple"); +// break; +// default: +// setStyleSheet("background-color:maroon"); +// break; +// } +// i++; + } void addButton(QAbstractButton *button, int priority) { m_layout->addButton(button, priority); } void setName(const QString &name) { + setObjectName(name); m_name = name; } QString name() const { return m_name; } void setButtonSize(QSize size) { m_layout->setButtonSize(size); } QSize iconSize() const { return m_layout->buttonSize(); } int visibleButtonCount() const { int count = 0; for(int i = m_layout->count()-1; i >= 0; --i) { if (! static_cast (m_layout->itemAt(i))->isEmpty()) ++count; } return count; } void setSeparator(Separators separators) { m_separators = separators; } Separators separators() const { return m_separators; } void setOrientation (Qt::Orientation orientation) { m_layout->setOrientation(orientation); } + +protected: private: SectionLayout *m_layout; QString m_name; Separators m_separators; }; Q_DECLARE_OPERATORS_FOR_FLAGS(Section::Separators) class KoToolBoxLayout : public QLayout { public: explicit KoToolBoxLayout(QWidget *parent) : QLayout(parent), m_orientation(Qt::Vertical), m_currentHeight(0) { setSpacing(6); } ~KoToolBoxLayout() { qDeleteAll( m_sections ); m_sections.clear(); } QSize sizeHint() const { if (m_sections.isEmpty()) return QSize(); QSize oneIcon = static_cast (m_sections[0]->widget())->iconSize(); return oneIcon; } QSize minimumSize() const { QSize s = sizeHint(); if (m_orientation == Qt::Vertical) { s.setHeight(m_currentHeight); } else { s.setWidth(m_currentHeight); } return s; } void addSection(Section *section) { addChildWidget(section); QList::iterator iterator = m_sections.begin(); int defaults = 2; // skip the first two as they are the 'main' and 'dynamic' sections. while (iterator != m_sections.end()) { if (--defaults < 0 && static_cast ((*iterator)->widget())->name() > section->name()) break; ++iterator; } m_sections.insert(iterator, new QWidgetItem(section)); } void addItem(QLayoutItem*) { Q_ASSERT(0); // don't let anything else be added. (code depends on this!) } QLayoutItem* itemAt(int i) const { if (m_sections.count() >= i) return 0; return m_sections.at(i); } + QLayoutItem* takeAt(int i) { return m_sections.takeAt(i); } + int count() const { return m_sections.count(); } void setGeometry (const QRect &rect) { // nothing to do? if (m_sections.isEmpty()) { m_currentHeight = 0; return; } // the names of the variables assume a vertical orientation, // but all calculations are done based on the real orientation const QSize iconSize = static_cast (m_sections.first()->widget())->iconSize(); const int maxWidth = (m_orientation == Qt::Vertical) ? rect.width() : rect.height(); // using min 1 as width to e.g. protect against div by 0 below const int iconWidth = qMax(1, (m_orientation == Qt::Vertical) ? iconSize.width() : iconSize.height()); const int iconHeight = qMax(1, (m_orientation == Qt::Vertical) ? iconSize.height() : iconSize.width()); const int maxColumns = qMax(1, (maxWidth / iconWidth)); int x = 0; int y = 0; bool firstSection = true; foreach (QWidgetItem *wi, m_sections) { Section *section = static_cast (wi->widget()); + // Since sections can overlap (if a section occupies two rows, and there + // is space on the second row for all of the next section, the next section + // will be placed overlapping with the previous section), it's important that + // later sections will be higher in the widget z-order than previous + // sections, so raise it. + section->raise(); const int buttonCount = section->visibleButtonCount(); if (buttonCount == 0) { // move out of view, not perfect TODO: better solution section->setGeometry(1000, 1000, 0, 0); continue; } // rows needed for the buttons (calculation gets the ceiling value of the plain div) const int neededRowCount = ((buttonCount-1) / maxColumns) + 1; const int availableButtonCount = (maxWidth - x + 1) / iconWidth; if (firstSection) { firstSection = false; } else if (buttonCount > availableButtonCount) { // start on a new row, set separator x = 0; y += iconHeight + spacing(); const Section::Separators separator = (m_orientation == Qt::Vertical) ? Section::SeparatorTop : Section::SeparatorLeft; section->setSeparator( separator ); } else { // append to last row, set separators (on first row only to the left side) const bool isFirstRow = (y == 0); const Section::Separators separators = isFirstRow ? ((m_orientation == Qt::Vertical) ? Section::SeparatorLeft : Section::SeparatorTop) : (Section::SeparatorTop | Section::SeparatorLeft); section->setSeparator( separators ); } const int usedColumns = qMin(buttonCount, maxColumns); if (m_orientation == Qt::Vertical) { section->setGeometry(x, y, usedColumns * iconWidth, neededRowCount * iconHeight); } else { section->setGeometry(y, x, neededRowCount * iconHeight, usedColumns * iconWidth); } // advance by the icons in the last row const int lastRowColumnCount = buttonCount - ((neededRowCount-1) * maxColumns); x += (lastRowColumnCount * iconWidth) + spacing(); // advance by all but the last used row y += (neededRowCount - 1) * iconHeight; } // cache total height (or width), adding the iconHeight for the current row m_currentHeight = y + iconHeight; } void setOrientation (Qt::Orientation orientation) { m_orientation = orientation; invalidate(); } private: QList m_sections; Qt::Orientation m_orientation; mutable int m_currentHeight; }; #endif