[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-core-devel
Subject:    Re: RFC: KToolBox
From:       "Matt Broadstone" <mbroadst () gmail ! com>
Date:       2006-09-02 21:06:24
Message-ID: 621909c70609021406p6f5d9a40yc923f1dd48756aea () mail ! gmail ! com
[Download RAW message or body]

Right - would probably help if I actually attached the class :)


On 9/2/06, Matt Broadstone <mbroadst@gmail.com> wrote:
> Attached is a replacement for the QToolBox that I have written. It's
> mainly intended to look MUCH better than the old tool box, but it has
> a few differences. Notably, there is no longer a concept of a "current
> page," as we had in QToolBox, also there is no longer a QScrollArea
> for each individual page in the toolbox, but instead one large one for
> the whole box. Lastly, you can choose a title and icon for the
> toolbox. Beyond that (sorry if I've forgotten anything) it tries to
> maintain as much of the api of the original QToolBox as possible so as
> to be somewhat of a drop-in replacement for it (not like ANYONE uses
> the QToolBox in kdelibs and kdebase, hopefully because it was too ugly
> :) ). Anyway - hope you all dig it, please let me know what you think
> can be improved, and hopefully we can get this in kdeui sooner than
> later.
>
> Matt Broadstone
>

["ktoolbox.h" (text/x-chdr)]

/*
    This file is part of the KDE libraries
    Copyright (C) 2006 Matt Broadstone <mbroadst@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

    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 KTOOLBOX_H
#define KTOOLBOX_H

#include <QFrame>

class KToolBox : public QFrame
{
    Q_OBJECT
public:
    KToolBox(QWidget *parent = 0);
    KToolBox(const QString &title, QWidget *parent = 0);
    KToolBox(const QString &title, const QIcon &icon, QWidget *parent = 0);
    ~KToolBox();

    int addItem(QWidget *widget, const QString &text);
    int addItem(QWidget *widget, const QIcon &icon, const QString &text);
    int insertItem(int index, QWidget *widget, const QString &text);
    int insertItem(int index, QWidget *widget, const QIcon &icon, const QString &text);

    void removeItem(int index);

    void setTitle(const QString &text);
    QString title() const;

    void setIcon(const QIcon &icon);
    QIcon icon() const;

    void setItemEnabled(int index, bool enabled);
    bool isItemEnabled(int index) const;

    void setItemIcon(int index, const QIcon &icon);
    QIcon itemIcon(int index) const;

    void setItemText(int index, const QString &text);
    QString itemText(int index) const;

    void setItemToolTip(int index, const QString &toolTip);
    QString itemToolTip(int index) const;

    QWidget *widget(int index) const;
    int indexOf(QWidget *widget) const;
    int count() const;

signals:
    void currentChanged(int index);
    void itemInserted(int index);
    void itemRemoved(int index);

protected:
    void showEvent(QShowEvent *e);
    void changeEvent(QEvent *);

private slots:
    void tabSelected();
    void widgetDestroyed(QObject*);

private:
    class Private;
    Private *d;
};

#endif

["ktoolbox.cpp" (text/x-c++src)]

/*
    This file is part of the KDE libraries
    Copyright (C) 2006 Matt Broadstone <mbroadst@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

    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 <QScrollArea>
#include <QVBoxLayout>
#include <QPainter>
#include <QPaintEvent>
#include <QPainterPath>
#include <QPen>
#include <QStyle>
#include <QStyleOptionToolBox>
#include <QApplication>
#include <QToolButton>
#include <QMenu>
#include <QDebug>

#include "ktoolbox.h"
#include "moc_ktoolbox.cpp"

class KToolBoxHeader : public QFrame
{
public:
    KToolBoxHeader(QWidget *parent = 0);

    QIcon icon() const { return m_icon; }
    void setIcon(const QIcon &icon) { m_icon = icon; }

    QString text() const { return m_text; }
    void setText(const QString &text) { m_text = text; }

    QSize sizeHint() const;
    QSize minimumSizeHint() const;

protected:
    void paintEvent(QPaintEvent *event);

private:
    QIcon m_icon;
    QString m_text;
    QMenu *m_menu;
    QToolButton *m_viewControl;
};

class KToolBoxBase : public QFrame
{
public:
    KToolBoxBase(QWidget *parent = 0);
protected:
    void paintEvent(QPaintEvent *event);
};

class KToolBoxFooter : public QFrame
{
public:
    KToolBoxFooter(QWidget *parent = 0);
protected:
    void paintEvent(QPaintEvent *event);
};

class KToolBoxTab : public QAbstractButton
{
public:
    KToolBoxTab(QWidget *parent = 0);

    void setSelected(bool);
    bool isSelected() const;

    QSize sizeHint() const;
    QSize minimumSizeHint() const;
protected:
    void paintEvent(QPaintEvent *);

private:
    bool selected;
};

class KToolBox::Private
{
public:
    struct Item
    {
        QFrame *base;
        QVBoxLayout *layout;
        KToolBoxTab *tab;
        QWidget *widget;

        inline void setText(const QString &text) { tab->setText(text); }
        inline void setIcon(const QIcon &icon) { tab->setIcon(icon); }
        inline void setToolTip(const QString &tip) { tab->setToolTip(tip); }
        inline QString text() const { return tab->text(); }
        inline QIcon icon() const { return tab->icon(); }
        inline QString toolTip() const { return tab->toolTip(); }

        inline bool operator==(const Item& other) const
        {
            return widget == other.widget;
        }
    };

    void init(const QString &title, const QIcon &icon, QFrame *parent);

    QList<Item> itemList;

    Item *item(QWidget *widget) const;
    const Item *item(int index) const;
    Item *item(int index);

    void relayout();
    void updateTabs();

    QVBoxLayout    *layout;

    KToolBoxHeader *header;
    KToolBoxBase   *base;
    KToolBoxFooter *footer;

    QScrollArea    *scrollArea;
    QFrame         *viewport;
    QVBoxLayout    *viewportLayout;
};

KToolBox::KToolBox(const QString &title, QWidget *parent)
    : QFrame(parent)
{
    d = new Private;
    d->init(title, QIcon(), this);
}

KToolBox::KToolBox(const QString &title, const QIcon &icon, QWidget *parent)
    : QFrame(parent)
{
    d = new Private;
    d->init(title, icon, this);
}

KToolBox::KToolBox(QWidget *parent)
    : QFrame(parent)
{
    d = new Private;
    d->init(QString(), QIcon(), this);
}

void KToolBox::Private::init(const QString &title, const QIcon &icon, QFrame *parent)
{
    header = new KToolBoxHeader;
    header->setText(title);
    header->setIcon(icon);

    base   = new KToolBoxBase;
    footer = new KToolBoxFooter;

    viewport = new QFrame;
    viewport->setFrameStyle(QFrame::NoFrame);

    scrollArea = new QScrollArea;
    scrollArea->setWidget(viewport);
    scrollArea->setWidgetResizable(true);
    scrollArea->setFrameStyle(QFrame::NoFrame);

    viewportLayout = new QVBoxLayout(viewport);
    viewportLayout->setMargin(0);
    viewportLayout->setSpacing(2);
    viewportLayout->addStretch();

    QVBoxLayout *baseLayout = new QVBoxLayout(base);
    baseLayout->setMargin(2);
    baseLayout->setSpacing(0);
    baseLayout->addWidget(scrollArea);

    layout = new QVBoxLayout(parent);
    layout->setMargin(0);
    layout->setSpacing(0);
    layout->addWidget(header);
    layout->addWidget(base);
    layout->addWidget(footer);

    parent->setBackgroundRole(QPalette::Base);
}

KToolBox::~KToolBox()
{
}

int KToolBox::addItem(QWidget *item, const QString &text)
{
    return insertItem(-1, item, QIcon(), text);
}

int KToolBox::addItem(QWidget *item, const QIcon &iconSet, const QString &text)
{
    return insertItem(-1, item, iconSet, text);
}

int KToolBox::insertItem(int index, QWidget *item, const QString &text)
{
    return insertItem(index, item, QIcon(), text);
}

int KToolBox::insertItem(int index, QWidget *widget, const QIcon &icon, const QString \
&text) {
    if (!widget)
        return -1;

    connect(widget, SIGNAL(destroyed(QObject*)), this, \
SLOT(widgetDestroyed(QObject*)));

    Private::Item page;
    page.widget = widget;
    page.tab = new KToolBoxTab;
    connect(page.tab, SIGNAL(clicked()), this, SLOT(tabSelected()));

    page.base = new QFrame;
    page.layout = new QVBoxLayout(page.base);
    page.layout->addWidget(widget);
    page.base->hide();
    page.base->setFrameStyle(QFrame::NoFrame);

    page.setText(text);
    page.setIcon(icon);

    if (index < 0 || index >= d->itemList.count())
    {
        index = d->itemList.count();
        d->itemList.append(page);

/*
        viewportLayout->addWidget(page.tab);
        viewportLayout->addWidget(page.base);
        viewportLayout->addStretch();
*/
    }
    else
    {
        d->itemList.insert(index, page);
//        relayout();
    }
    d->relayout();


    page.tab->show();
    d->updateTabs();
    return index;
}

void KToolBox::tabSelected()
{
    KToolBoxTab *tab = qobject_cast<KToolBoxTab*>(sender());
    QFrame *base = 0;

    foreach (Private::Item page, d->itemList)
    {
        if (page.tab == tab)
        {
            base = page.base;
            break;
        }
    }

    if (tab->isSelected())
    {
        tab->setSelected(false);
        base->hide();
    }
    else
    {
        tab->setSelected(true);
        base->show();
    }
    d->updateTabs();
}

void KToolBox::widgetDestroyed(QObject *object)
{
    QWidget *p = (QWidget*)object;

    Private::Item *c = d->item(p);
    if (!p || !c)
        return;

    d->viewportLayout->removeWidget(c->base);
    d->viewportLayout->removeWidget(c->tab);
    c->base->deleteLater();
    delete c->tab;

    d->itemList.removeAll(*c);
}

void KToolBox::removeItem(int index)
{
    if (QWidget *w = widget(index))
    {
        disconnect(w, SIGNAL(destroyed(QObject*)), this, \
SLOT(widgetDestroyed(QObject*)));  w->setParent(this);
        widgetDestroyed(w);
    }
}

QWidget *KToolBox::widget(int index) const
{
    if (index < 0 || index >= (int)d->itemList.size())
        return 0;
    return d->itemList.at(index).widget;
}

int KToolBox::indexOf(QWidget *widget) const
{
    Private::Item *c = d->item(widget);
    return c ? d->itemList.indexOf(*c) : -1;
}

void KToolBox::setItemEnabled(int index, bool enabled)
{
    Private::Item *c = d->item(index);
    if (!c)
        return;

    c->tab->setEnabled(enabled);
}

void KToolBox::setTitle(const QString &text)
{
    if (d->header)
        d->header->setText(text);
}

void KToolBox::setIcon(const QIcon &icon)
{
    if (d->header)
        d->header->setIcon(icon);
}


void KToolBox::setItemText(int index, const QString &text)
{
    Private::Item *c = d->item(index);
    if (c)
        c->setText(text);
}

void KToolBox::setItemToolTip(int index, const QString &toolTip)
{
    Private::Item *c = d->item(index);
    if (c)
        c->setToolTip(toolTip);
}

bool KToolBox::isItemEnabled(int index) const
{
    const Private::Item *c = d->item(index);
    return c && c->tab->isEnabled();
}

QString KToolBox::title() const
{
    return (d->header ? d->header->text() : QString());
}

QIcon KToolBox::icon() const
{
    return (d->header ? d->header->icon() : QIcon());
}

QString KToolBox::itemText(int index) const
{
    const Private::Item *c = d->item(index);
    return (c ? c->text() : QString());
}

QString KToolBox::itemToolTip(int index) const
{
    const Private::Item *c = d->item(index);
    return (c ? c->toolTip() : QString());
}

void KToolBox::showEvent(QShowEvent *e)
{
    QWidget::showEvent(e);
}

void KToolBox::changeEvent(QEvent *ev)
{
    if(ev->type() == QEvent::StyleChange)
        d->updateTabs();
    QFrame::changeEvent(ev);
}

void KToolBox::Private::relayout()
{
    delete viewportLayout;
    viewportLayout = new QVBoxLayout(viewport);
    viewportLayout->setMargin(0);
    viewportLayout->setSpacing(1);
    foreach (Item page, itemList)
    {
        viewportLayout->addWidget(page.tab);
        viewportLayout->addWidget(page.base);
    }
    viewportLayout->addStretch();
}

void KToolBox::Private::updateTabs()
{
    foreach (Item page, itemList)
    {
        KToolBoxTab *tB = page.tab;
        QWidget *tW = page.widget;
        {
            QPalette p = tB->palette();
            p.setColor(tB->backgroundRole(), \
tW->palette().color(tW->backgroundRole()));  tB->setPalette(p);
            tB->update();
        }
    }
}

KToolBox::Private::Item *KToolBox::Private::item(QWidget *widget) const
{
    if (!widget)
        return 0;

    foreach (Item i, itemList)
    {
        if (i.widget == widget)
            return &i;
    }
    return 0;
}

KToolBox::Private::Item *KToolBox::Private::item(int index)
{
    if (index >= 0 && index < itemList.size())
        return &itemList[index];
    return 0;
}

const KToolBox::Private::Item *KToolBox::Private::item(int index) const
{
    if (index >= 0 && index < itemList.size())
        return &itemList.at(index);
    return 0;
}

KToolBoxHeader::KToolBoxHeader(QWidget *parent)
    : QFrame(parent)
{
    setForegroundRole(QPalette::Base);
    QFont f(font());
    f.setPointSizeF(8.0);
    setFont(f);
}

QSize KToolBoxHeader::sizeHint() const
{
    QSize iconSize(8, 8);
    if (!m_icon.isNull())
    {
        int icone = style()->pixelMetric(QStyle::PM_SmallIconSize);
        iconSize += QSize(icone + 2, icone);
    }
    QSize textSize = fontMetrics().size(Qt::TextShowMnemonic, m_text) + QSize(0, 8);

    QSize total(iconSize.width() + textSize.width(), qMax(iconSize.height(), \
textSize.height()));  return total.expandedTo(QApplication::globalStrut());
}

QSize KToolBoxHeader::minimumSizeHint() const
{
    if (m_icon.isNull())
        return QSize();
    int icone = style()->pixelMetric(QStyle::PM_SmallIconSize);
    return QSize(icone + 8, icone + 8);
}

void KToolBoxHeader::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    QRegion space(0, 0, event->rect().width(), event->rect().height());
    // top-left
    space -= (QRegion( 0, 0, 1, 2 ) + QRegion( 1, 0, 1, 1 ));
    // top-right
    space -= (QRegion( event->rect().width() - 1, 0, 1, 2 ) + QRegion( \
event->rect().width() - 2, 0, 1, 1 ));

    painter.save();
    painter.setClipRegion(space);
    painter.fillRect(0, 0, event->rect().width(), event->rect().height(), \
Qt::darkGray);

/*
    QRect textRect = QRect(event->rect().height(), event->rect().height() / 2, \
event->rect().width() - event->rect().height(), event->rect().height());  \
                painter.drawText(textRect, m_text);
*/
    painter.restore();

    QPainter *p = &painter;
    const QPalette &pal = palette();
    QStyleOptionToolBox opt;
    opt.init(this);
//    if (selected)
        opt.state |= QStyle::State_Selected;
//    if (isDown())
        opt.state |= QStyle::State_Sunken;
    opt.text = m_text;
    opt.icon = m_icon;
//    style()->drawControl(QStyle::CE_ToolBoxTab, &opt, p, parentWidget());

    QPixmap pm = m_icon.pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize), \
QIcon::Normal);

    QRect cr = style()->subElementRect(QStyle::SE_ToolBoxTabContents, &opt, this);
    QRect tr, ir;
    int ih = 0;
    if (pm.isNull()) {
        tr = cr;
        tr.adjust(4, 0, -8, 0);
    } else {
        int iw = pm.width() + 4;
        ih = pm.height();
        ir = QRect(cr.left() + 4, cr.top(), iw + 2, ih);
        tr = QRect(ir.right(), cr.top(), cr.width() - ir.right() - 4, cr.height());
    }

    QFont f(p->font());
    f.setBold(true);
    p->setFont(f);

//    QString txt = fontMetrics().elidedText(text, Qt::ElideRight, tr.width());
    QString txt = m_text;

    if (ih)
        p->drawPixmap(ir.left(), (height() - ih) / 2, pm);

    int alignment = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic;
    if (!style()->styleHint(QStyle::SH_UnderlineShortcut, 0, this))
        alignment |= Qt::TextHideMnemonic;
    style()->drawItemText(p, tr, alignment, pal, true, txt, foregroundRole());
}

KToolBoxBase::KToolBoxBase(QWidget *parent)
    : QFrame(parent)
{}

void KToolBoxBase::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.fillRect(event->rect(), palette().base());

    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(QPen(Qt::darkGray, 3, Qt::SolidLine, Qt::RoundCap, \
Qt::RoundJoin));

    painter.drawLine(event->rect().topLeft(), event->rect().bottomLeft());
    painter.drawLine(event->rect().topRight(), event->rect().bottomRight());
}

KToolBoxFooter::KToolBoxFooter(QWidget *parent)
    : QFrame(parent)
{
    setFixedHeight(15);
    setBackgroundRole(QPalette::Base);
}

void KToolBoxFooter::paintEvent(QPaintEvent *event)
{
    QPainterPath path;
    QRect rect(event->rect());

    path.moveTo(rect.left(), rect.top());
    path.lineTo(rect.left(), rect.bottom() - 5.0);
    path.arcTo(rect.left(), rect.bottom() - 10.0, 10.0, 10.0, 180.0, 90.0);
    path.lineTo(rect.right() - 5.0, rect.bottom());
    path.arcTo(rect.right() - 10.0, rect.bottom() - 10.0, 10.0, 10.0, 270.0, 90.0);
    path.lineTo(rect.right(), rect.top());

    QPainter painter(this);
    painter.fillRect(rect, palette().base());
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(QPen(Qt::darkGray, 3, Qt::SolidLine, Qt::RoundCap, \
Qt::RoundJoin));  painter.drawPath(path);
}


KToolBoxTab::KToolBoxTab(QWidget *parent)
    : QAbstractButton(parent),
      selected(false)
{
    setBackgroundRole(QPalette::Window);
    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
    setFocusPolicy(Qt::NoFocus);

    QFont tmp(font());
    tmp.setPointSizeF(8.0);
    setFont(tmp);
}

void KToolBoxTab::setSelected(bool b)
{
    selected = b;
    update();
}

bool KToolBoxTab::isSelected() const
{
    return (selected == true);
}

QSize KToolBoxTab::sizeHint() const
{
    QSize iconSize(8, 8);
    if (!icon().isNull())
    {
        int icone = style()->pixelMetric(QStyle::PM_SmallIconSize);
        iconSize += QSize(icone + 2, icone);
    }
    QSize textSize = fontMetrics().size(Qt::TextShowMnemonic, text()) + QSize(0, 8);

    QSize total(iconSize.width() + textSize.width(), qMax(iconSize.height(), \
textSize.height()));  return total.expandedTo(QApplication::globalStrut());
}

QSize KToolBoxTab::minimumSizeHint() const
{
    if (icon().isNull())
        return QSize();
    int icone = style()->pixelMetric(QStyle::PM_SmallIconSize);
    return QSize(icone + 8, icone + 8);
}

void KToolBoxTab::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    QString text = QAbstractButton::text();
    const QPalette &pal = palette();

    painter.fillRect(event->rect(), Qt::lightGray);

    QStyleOption opt;
    opt.init(this);
    opt.rect = QRect(0,( height() - sizeHint().height() )/2, sizeHint().height(), \
sizeHint().height());

    if (selected)
    {
        QFont font(painter.font());
        font.setBold(true);
        painter.setFont(font);

        style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &opt, &painter);
    }
    else
        style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &opt, &painter);


    QRect textRect = QRect(sizeHint().height(), (height() - sizeHint().height()) / 2, \
event->rect().width(), event->rect().height());



//    QString txt = fontMetrics().elidedText(text, Qt::ElideRight, tr.width());
    QString txt = text;

    int alignment = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic;
    style()->drawItemText(&painter, textRect, alignment, pal, isEnabled(), txt, \
foregroundRole());

    if (!txt.isEmpty() && hasFocus())
    {
        QStyleOptionFocusRect opt;
        opt.rect = textRect;
        opt.palette = pal;
        opt.state = QStyle::State_None;
        style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &painter, this);
    }
}



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic