diff --git a/libs/widgets/KoDockWidgetTitleBar.cpp b/libs/widgets/KoDockWidgetTitleBar.cpp index b34e60f74a..c5ce2ead36 100644 --- a/libs/widgets/KoDockWidgetTitleBar.cpp +++ b/libs/widgets/KoDockWidgetTitleBar.cpp @@ -1,379 +1,379 @@ /* This file is part of the KDE project Copyright (c) 2007 Marijn Kruisselbrink Copyright (C) 2007 Thomas Zander 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 "KoDockWidgetTitleBar.h" #include "KoDockWidgetTitleBar_p.h" #include "KoDockWidgetTitleBarButton.h" #include #include #include #include #include #include #include #include #include #include #include static inline bool hasFeature(const QDockWidget *dockwidget, QDockWidget::DockWidgetFeature feature) { return (dockwidget->features() & feature) == feature; } KoDockWidgetTitleBar::KoDockWidgetTitleBar(QDockWidget* dockWidget) : QWidget(dockWidget), d(new Private(this)) { QDockWidget *q = dockWidget; d->floatIcon = kisIcon("docker_float"); d->floatButton = new KoDockWidgetTitleBarButton(this); d->floatButton->setIcon(d->floatIcon); connect(d->floatButton, SIGNAL(clicked()), SLOT(toggleFloating())); d->floatButton->setVisible(true); d->floatButton->setToolTip(i18nc("@info:tooltip", "Float Docker")); d->floatButton->setStyleSheet("border: 0"); d->removeIcon = kisIcon("docker_close"); d->closeButton = new KoDockWidgetTitleBarButton(this); d->closeButton->setIcon(d->removeIcon); connect(d->closeButton, SIGNAL(clicked()), q, SLOT(close())); d->closeButton->setVisible(true); d->closeButton->setToolTip(i18nc("@info:tooltip", "Close Docker")); d->closeButton->setStyleSheet("border: 0"); // border makes the header busy looking (appears on some OSs) d->openIcon = kisIcon("docker_collapse_a"); d->closeIcon = kisIcon("docker_collapse_b"); d->collapseButton = new KoDockWidgetTitleBarButton(this); d->collapseButton->setIcon(d->openIcon); connect(d->collapseButton, SIGNAL(clicked()), SLOT(toggleCollapsed())); d->collapseButton->setVisible(true); d->collapsable = true; d->collapseButton->setToolTip(i18nc("@info:tooltip", "Collapse Docker")); d->collapseButton->setStyleSheet("border: 0"); d->lockIcon = kisIcon("docker_lock_a"); d->lockButton = new KoDockWidgetTitleBarButton(this); d->lockButton->setCheckable(true); d->lockButton->setIcon(d->lockIcon); connect(d->lockButton, SIGNAL(toggled(bool)), SLOT(setLocked(bool))); d->lockButton->setVisible(true); d->lockable = true; d->lockButton->setToolTip(i18nc("@info:tooltip", "Lock Docker")); d->lockButton->setStyleSheet("border: 0"); connect(dockWidget, SIGNAL(featuresChanged(QDockWidget::DockWidgetFeatures)), SLOT(featuresChanged(QDockWidget::DockWidgetFeatures))); connect(dockWidget, SIGNAL(topLevelChanged(bool)), SLOT(topLevelChanged(bool))); d->featuresChanged(0); } KoDockWidgetTitleBar::~KoDockWidgetTitleBar() { delete d; } QSize KoDockWidgetTitleBar::minimumSizeHint() const { return sizeHint(); } QSize KoDockWidgetTitleBar::sizeHint() const { if (isHidden()) { return QSize(0, 0); } QDockWidget *q = qobject_cast(parentWidget()); int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q); int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q); // get size of buttons... QSize closeSize(0, 0); if (d->closeButton && hasFeature(q, QDockWidget::DockWidgetClosable)) { closeSize = d->closeButton->sizeHint(); } QSize floatSize(0, 0); if (d->floatButton && hasFeature(q, QDockWidget::DockWidgetFloatable)) { floatSize = d->floatButton->sizeHint(); } QSize hideSize(0, 0); if (d->collapseButton && d->collapsable) { hideSize = d->collapseButton->sizeHint(); } QSize lockSize(0, 0); if (d->lockButton && d->lockable) { lockSize = d->lockButton->sizeHint(); } int buttonHeight = qMax(qMax(qMax(closeSize.height(), floatSize.height()), hideSize.height()), lockSize.height()) + 2; int buttonWidth = closeSize.width() + floatSize.width() + hideSize.width() + lockSize.width(); int height = buttonHeight; if (d->textVisibilityMode == FullTextAlwaysVisible) { // get font size QFontMetrics titleFontMetrics = q->fontMetrics(); int fontHeight = titleFontMetrics.lineSpacing() + 2 * mw; height = qMax(height, fontHeight); } /* * Calculate the width of title and add to the total width of the docker window when collapsed. */ const int titleWidth = (d->textVisibilityMode == FullTextAlwaysVisible) ? (q->fontMetrics().width(q->windowTitle()) + 2*mw) : 0; if (d->preCollapsedWidth > 0) { return QSize(d->preCollapsedWidth, height); } else { if (d->textVisibilityMode == FullTextAlwaysVisible) { return QSize(buttonWidth /*+ height*/ + 2*mw + 2*fw + titleWidth, height); } else { if (q->widget()) { - return QSize(qMin(q->widget()->sizeHint().width(), buttonWidth), height); + return QSize(qMin(q->widget()->minimumSizeHint().width(), buttonWidth), height); } else { return QSize(buttonWidth, height); } } } } void KoDockWidgetTitleBar::paintEvent(QPaintEvent*) { QStylePainter p(this); QDockWidget *q = qobject_cast(parentWidget()); int fw = q->isFloating() ? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q) : 0; int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q); QStyleOptionDockWidget titleOpt; titleOpt.initFrom(q); QSize collapseButtonSize(0,0); if (d->collapsable && d->collapseButton->isVisible()) { collapseButtonSize = d->collapseButton->size(); } QSize lockButtonSize(0,0); if (d->lockable && d->lockButton->isVisible()) { lockButtonSize = d->lockButton->size(); } titleOpt.rect = QRect(QPoint(fw + mw + collapseButtonSize.width() + lockButtonSize.width(), 0), QSize(geometry().width() - (fw * 2) - mw - collapseButtonSize.width() - lockButtonSize.width(), geometry().height())); titleOpt.title = q->windowTitle(); titleOpt.closable = hasFeature(q, QDockWidget::DockWidgetClosable); titleOpt.floatable = hasFeature(q, QDockWidget::DockWidgetFloatable); p.drawControl(QStyle::CE_DockWidgetTitle, titleOpt); } void KoDockWidgetTitleBar::resizeEvent(QResizeEvent*) { QDockWidget *q = qobject_cast(parentWidget()); int fw = q->isFloating() ? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q) : 0; QStyleOptionDockWidget opt; opt.initFrom(q); opt.rect = QRect(QPoint(fw, fw), QSize(geometry().width() - (fw * 2), geometry().height() - (fw * 2))); opt.title = q->windowTitle(); opt.closable = hasFeature(q, QDockWidget::DockWidgetClosable); opt.floatable = hasFeature(q, QDockWidget::DockWidgetFloatable); QRect floatRect = q->style()->subElementRect(QStyle::SE_DockWidgetFloatButton, &opt, q); if (!floatRect.isNull()) d->floatButton->setGeometry(floatRect); QRect closeRect = q->style()->subElementRect(QStyle::SE_DockWidgetCloseButton, &opt, q); if (!closeRect.isNull()) d->closeButton->setGeometry(closeRect); int top = fw; if (!floatRect.isNull()) top = floatRect.y(); else if (!closeRect.isNull()) top = closeRect.y(); QSize size = d->collapseButton->size(); if (!closeRect.isNull()) { size = d->closeButton->size(); } else if (!floatRect.isNull()) { size = d->floatButton->size(); } QRect collapseRect = QRect(QPoint(fw, top), size); d->collapseButton->setGeometry(collapseRect); size = d->lockButton->size(); if (!closeRect.isNull()) { size = d->closeButton->size(); } else if (!floatRect.isNull()) { size = d->floatButton->size(); } const QSize lockRectSize = size; if (q->isFloating() || (width() < (closeRect.width() + lockRectSize.width()) + 50)) { d->collapsable = false; d->collapseButton->setVisible(false); d->lockButton->setVisible(false); d->lockable = false; } else { d->collapsable = d->collapsableSet; d->collapseButton->setVisible(d->collapsableSet); d->lockButton->setVisible(true); d->lockable = true; } int offset = 0; if (d->collapsable) { offset = collapseRect.width(); } QRect lockRect = QRect(QPoint(fw + 2 + offset, top), lockRectSize); d->lockButton->setGeometry(lockRect); } void KoDockWidgetTitleBar::setCollapsed(bool collapsed) { QDockWidget *q = qobject_cast(parentWidget()); if (q && q->widget() && q->widget()->isHidden() != collapsed) d->toggleCollapsed(); } void KoDockWidgetTitleBar::setLocked(bool locked) { QDockWidget *q = qobject_cast(parentWidget()); d->locked = locked; d->lockButton->blockSignals(true); d->lockButton->setChecked(locked); d->lockButton->blockSignals(false); //qDebug() << "setlocked" << q << d->locked << locked; if (locked) { d->features = q->features(); q->setFeatures(QDockWidget::NoDockWidgetFeatures); } else { q->setFeatures(d->features); } q->toggleViewAction()->setEnabled(!locked); d->closeButton->setEnabled(!locked); d->floatButton->setEnabled(!locked); d->collapseButton->setEnabled(!locked); d->updateIcons(); q->setProperty("Locked", locked); resizeEvent(0); } void KoDockWidgetTitleBar::setCollapsable(bool collapsable) { d->collapsableSet = collapsable; d->collapsable = collapsable; d->collapseButton->setVisible(collapsable); } void KoDockWidgetTitleBar::setTextVisibilityMode(TextVisibilityMode textVisibilityMode) { d->textVisibilityMode = textVisibilityMode; } void KoDockWidgetTitleBar::updateIcons() { d->updateIcons(); } void KoDockWidgetTitleBar::Private::toggleFloating() { QDockWidget *q = qobject_cast(thePublic->parentWidget()); q->setFloating(!q->isFloating()); updateIcons(); } void KoDockWidgetTitleBar::Private::topLevelChanged(bool topLevel) { lockButton->setEnabled(!topLevel); updateIcons(); } void KoDockWidgetTitleBar::Private::toggleCollapsed() { QDockWidget *q = qobject_cast(thePublic->parentWidget()); if (q == 0) // there does not *have* to be anything on the dockwidget. return; preCollapsedWidth = q->widget()->isHidden() ? -1 : thePublic->width(); q->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); // will be overwritten again next if (q->widget()) { q->widget()->setVisible(q->widget()->isHidden()); collapseButton->setIcon(q->widget()->isHidden() ? kisIcon("docker_collapse_b") : kisIcon("docker_collapse_a")); } } void KoDockWidgetTitleBar::Private::featuresChanged(QDockWidget::DockWidgetFeatures) { QDockWidget *q = qobject_cast(thePublic->parentWidget()); closeButton->setVisible(hasFeature(q, QDockWidget::DockWidgetClosable)); floatButton->setVisible(hasFeature(q, QDockWidget::DockWidgetFloatable)); thePublic->resizeEvent(0); } void KoDockWidgetTitleBar::Private::updateIcons() { QDockWidget *q = qobject_cast(thePublic->parentWidget()); lockIcon = (!locked) ? kisIcon("docker_lock_a") : kisIcon("docker_lock_b"); lockButton->setIcon(lockIcon); // this method gets called when switching themes, so update all of the themed icons now floatButton->setIcon(kisIcon("docker_float")); closeButton->setIcon(kisIcon("docker_close")); if (q->widget()) { collapseButton->setIcon(q->widget()->isHidden() ? kisIcon("docker_collapse_b") : kisIcon("docker_collapse_a")); } thePublic->resizeEvent(0); } //have to include this because of Q_PRIVATE_SLOT #include "moc_KoDockWidgetTitleBar.cpp" diff --git a/libs/widgets/KoToolBoxLayout_p.h b/libs/widgets/KoToolBoxLayout_p.h index 9c31758c0c..4616808711 100644 --- a/libs/widgets/KoToolBoxLayout_p.h +++ b/libs/widgets/KoToolBoxLayout_p.h @@ -1,434 +1,441 @@ /* * 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() override { qDeleteAll( m_items ); m_items.clear(); } void addButton(QAbstractButton *button, int priority) { addChildWidget(button); if (m_priorities.values().contains(priority)) { qWarning() << "Button" << button << "has a conflicting priority"; } 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 override { Q_ASSERT(0); return QSize(); } void addItem(QLayoutItem*) override { Q_ASSERT(0); } QLayoutItem* itemAt(int i) const override { if (m_items.count() <= i) return 0; return m_items.at(i); } QLayoutItem* takeAt(int i) override { return m_items.takeAt(i); } int count() const override { return m_items.count(); } void setGeometry (const QRect &rect) override { 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) { setSpacing(6); } ~KoToolBoxLayout() override { qDeleteAll( m_sections ); m_sections.clear(); } QSize sizeHint() const override { - return minimumSize(); + // Prefer showing two rows/columns by default + QSize twoIcons = static_cast (m_sections[0]->widget())->iconSize() * 2; + const int length = doLayout(QRect(QPoint(), twoIcons), false); + if (m_orientation == Qt::Vertical) { + return QSize(twoIcons.width(), length); + } else { + return QSize(length, twoIcons.height()); + } } QSize minimumSize() const override { if (m_sections.isEmpty()) return QSize(); QSize oneIcon = static_cast (m_sections[0]->widget())->iconSize(); return oneIcon; } 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*) override { Q_ASSERT(0); // don't let anything else be added. (code depends on this!) } QLayoutItem* itemAt(int i) const override { return m_sections.value(i); } QLayoutItem* takeAt(int i) override { return m_sections.takeAt(i); } int count() const override { return m_sections.count(); } void setGeometry (const QRect &rect) override { QLayout::setGeometry(rect); doLayout(rect, true); } bool hasHeightForWidth() const override { // return true; return m_orientation == Qt::Vertical; } int heightForWidth(int width) const override { if (m_orientation == Qt::Vertical) { const int height = doLayout(QRect(0, 0, width, 0), false); return height; } else { #if 0 const int iconHeight = static_cast (m_sections[0]->widget())->iconSize().height(); for (int i = 1; i <= 10; i++) { const int testWidth = doLayout(QRect(0, 0, 0, iconHeight * i), false); if (testWidth <= width) { return iconHeight * i; } } // Return a huge height return 65535; #endif return -1; } } /** * For calculating the width from height by KoToolBoxScrollArea. * QWidget doesn't actually support trading width for height, so it needs to * be handled specificly. */ int widthForHeight(int height) const { if (m_orientation == Qt::Horizontal) { const int width = doLayout(QRect(0, 0, 0, height), false); return width; } else { return -1; } } void setOrientation (Qt::Orientation orientation) { m_orientation = orientation; invalidate(); } private: int doLayout(const QRect &rect, bool notDryRun) const { // nothing to do? if (m_sections.isEmpty()) { return 0; } // the names of the variables assume a vertical orientation, // but all calculations are done based on the real orientation const bool isVertical = m_orientation == Qt::Vertical; const QSize iconSize = static_cast (m_sections.first()->widget())->iconSize(); const int maxWidth = isVertical ? rect.width() : rect.height(); // using min 1 as width to e.g. protect against div by 0 below const int iconWidth = qMax(1, isVertical ? iconSize.width() : iconSize.height()); const int iconHeight = qMax(1, isVertical ? 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()); const int buttonCount = section->visibleButtonCount(); if (buttonCount == 0) { // move out of view, not perfect TODO: better solution if (notDryRun) { 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; if (firstSection) { firstSection = false; } else { // start on a new row, set separator x = 0; y += iconHeight + spacing(); if (notDryRun){ const Section::Separators separator = isVertical ? Section::SeparatorTop : Section::SeparatorLeft; section->setSeparator( separator ); } } if (notDryRun) { const int usedColumns = qMin(buttonCount, maxColumns); if (isVertical) { 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 return y + iconHeight; } QList m_sections; Qt::Orientation m_orientation; }; #endif