/* * Cantata * * Copyright (c) 2011-2022 Craig Drummond * * ---- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "configdialog.h" #include "utils.h" #ifdef __APPLE__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "icon.h" #include "osxstyle.h" #include "windowmanager.h" #else #include "pagewidget.h" #endif #ifdef __APPLE__ //#define ANIMATE_RESIZE static void drawFadedLine(QPainter *p, const QRect &r, const QColor &col, bool horiz, double fadeSize) { QPointF start(r.x()+0.5, r.y()+0.5); QPointF end(r.x()+(horiz ? r.width()-1 : 0)+0.5, r.y()+(horiz ? 0 : r.height()-1)+0.5); QLinearGradient grad(start, end); QColor fade(col); fade.setAlphaF(0.0); grad.setColorAt(0, fade); grad.setColorAt(fadeSize, col); grad.setColorAt(1.0-fadeSize, col); grad.setColorAt(1, fade); p->setPen(QPen(QBrush(grad), 1)); p->drawLine(start, end); } class ConfigDialogToolButton : public QToolButton { public: ConfigDialogToolButton(QWidget *p) : QToolButton(p) { setStyleSheet("QToolButton {border: 0}"); setMinimumWidth(56); } void paintEvent(QPaintEvent *e) { if (isChecked()) { QPainter p(this); QColor col(Qt::black); QRect r(rect()); if (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 9)) { col.setAlphaF(0.1); p.setClipRect(r); p.setRenderHint(QPainter::Antialiasing, true); p.fillPath(Utils::buildPath(r.adjusted(0, 0, 0, 32), 4), col); } else { QLinearGradient bgndGrad(r.topLeft(), r.bottomLeft()); col.setAlphaF(0.01); bgndGrad.setColorAt(0, col); bgndGrad.setColorAt(1, col); col.setAlphaF(0.1); bgndGrad.setColorAt(0.25, col); bgndGrad.setColorAt(0.75, col); p.fillRect(r, bgndGrad); p.setRenderHint(QPainter::Antialiasing, true); col.setAlphaF(0.25); drawFadedLine(&p, QRect(r.x(), r.y(), r.x(), r.y()+r.height()-1), col, false, 0.25); drawFadedLine(&p, QRect(r.x()+r.width()-1, r.y(), r.x()+1, r.y()+r.height()-1), col, false, 0.25); } } QStylePainter p(this); QStyleOptionToolButton opt; initStyleOption(&opt); if (isDown()) { opt.state&=QStyle::State_Sunken; } p.drawComplexControl(QStyle::CC_ToolButton, opt); } }; static const int constMinPad=16; #endif ConfigDialog::ConfigDialog(QWidget *parent, const QString &name, const QSize &defSize, bool instantApply) #ifdef __APPLE__ : QMainWindow(parent) , shown(false) , resizeAnim(0) #else : Dialog(parent, name, defSize) #endif { instantApply=false; // TODO!!!! #ifdef __APPLE__ Q_UNUSED(defSize) Q_UNUSED(name) setUnifiedTitleAndToolBarOnMac(true); toolBar=addToolBar("ToolBar"); toolBar->setMovable(false); toolBar->setContextMenuPolicy(Qt::PreventContextMenu); toolBar->setStyleSheet("QToolBar { spacing:0px} "); QWidget *left=new QWidget(toolBar); left->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); toolBar->addWidget(left); QWidget *right=new QWidget(toolBar); right->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); rightSpacer=toolBar->addWidget(right); left->setMinimumWidth(constMinPad); right->setMinimumWidth(constMinPad); QWidget *mw=new QWidget(this); QBoxLayout *lay=new QBoxLayout(QBoxLayout::TopToBottom, mw); stack=new QStackedWidget(mw); group=new QButtonGroup(toolBar); lay->addWidget(stack); if (instantApply) { buttonBox=0; setWindowFlags(Qt::Window|Qt::WindowTitleHint|Qt::CustomizeWindowHint|Qt::WindowMaximizeButtonHint|Qt::WindowCloseButtonHint); } else { buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::Apply, Qt::Horizontal, mw); lay->addWidget(buttonBox); connect(buttonBox, SIGNAL(clicked(QAbstractButton *)), this, SLOT(macButtonPressed(QAbstractButton *))); buttonBox->setStyle(Dialog::buttonProxyStyle()); // Hide window buttons if not instany apply - dont want user just closing dialog setWindowFlags(Qt::Window|Qt::WindowTitleHint|Qt::CustomizeWindowHint|Qt::WindowMaximizeButtonHint); } group->setExclusive(true); setCentralWidget(mw); WindowManager *wm=new WindowManager(toolBar); wm->initialize(WindowManager::WM_DRAG_MENU_AND_TOOLBAR); wm->registerWidgetAndChildren(toolBar); #else bool limitedHeight=Utils::limitedHeight(this); pageWidget = new PageWidget(this, limitedHeight, !limitedHeight); setMainWidget(pageWidget); if (instantApply) { // TODO: What??? } else { setButtons(Dialog::Ok|Dialog::Cancel|Dialog::Apply); } #endif } ConfigDialog::~ConfigDialog() { #ifdef __APPLE__ OSXStyle::self()->removeWindow(this); #endif } void ConfigDialog::addPage(const QString &id, QWidget *widget, const QString &name, const QIcon &icon, const QString &header) { #ifdef __APPLE__ Page newPage; newPage.item=new ConfigDialogToolButton(this); newPage.item->setText(name); newPage.item->setIcon(icon); newPage.item->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); newPage.item->setCheckable(true); newPage.item->setChecked(false); newPage.item->setProperty("id", id); newPage.item->ensurePolished(); toolBar->insertWidget(rightSpacer, newPage.item); newPage.widget=widget; newPage.index=stack->count(); stack->addWidget(widget); group->addButton(newPage.item); pages.insert(id, newPage); connect(newPage.item, SIGNAL(toggled(bool)), this, SLOT(activatePage())); int sz=0; QMap::const_iterator it=pages.begin(); QMap::const_iterator end=pages.end(); for (; it!=end; ++it) { sz+=it.value().item->sizeHint().width()+4; } setMinimumWidth(sz+(2*constMinPad)+48); #else pages.insert(id, pageWidget->addPage(widget, name, icon, header)); #endif } bool ConfigDialog::setCurrentPage(const QString &id) { if (!pages.contains(id)) { return false; } #ifdef __APPLE__ QMap::const_iterator it=pages.find(id); if (it!=pages.end()) { stack->setCurrentIndex(it.value().index); setCaption(it.value().item->text()); it.value().item->setChecked(true); } #ifdef ANIMATE_RESIZE if (isVisible()) { it.value().widget->ensurePolished(); ensurePolished(); } int newH=buttonBox ? (it.value().widget->sizeHint().height()+toolBar->height()+buttonBox->height()+layout()->spacing()+(2*layout()->margin())) : (it.value().widget->sizeHint().height()+toolBar->height()+(2*layout()->margin())); if (isVisible()) { if (newH!=height()) { if (!resizeAnim) { resizeAnim=new QPropertyAnimation(this, "h"); resizeAnim->setDuration(250); } resizeAnim->setStartValue(height()); resizeAnim->setEndValue(newH); resizeAnim->start(); } } else { setFixedHeight(newH); resize(width(), newH); setMaximumHeight(QWIDGETSIZE_MAX); } #endif #else pageWidget->setCurrentPage(pages[id]); #endif return true; } #ifdef __APPLE__ void ConfigDialog::setH(int h) { if (resizeAnim && h==resizeAnim->endValue().toInt()) { setMaximumHeight(QWIDGETSIZE_MAX); resize(width(), h); } else { setFixedHeight(h); } } #endif QWidget * ConfigDialog::getPage(const QString &id) const { if (!pages.contains(id)) { return nullptr; } #ifdef __APPLE__ return pages[id].widget; #else return pages[id]->widget(); #endif } void ConfigDialog::slotButtonClicked(int button) { switch (button) { case Dialog::Ok: case Dialog::Apply: save(); break; case Dialog::Cancel: cancel(); reject(); #ifndef __APPLE__ // Need to call this - if not, when dialog is closed by window X control, it is not deleted!!!! Dialog::slotButtonClicked(button); #endif break; default: break; } if (Dialog::Ok==button) { accept(); } #ifndef __APPLE__ Dialog::slotButtonClicked(button); #endif } void ConfigDialog::activatePage() { #ifdef __APPLE__ QToolButton *item=qobject_cast(sender()); if (item && item->isChecked()) { setCurrentPage(item->property("id").toString()); } #endif } #ifdef __APPLE__ void ConfigDialog::accept() { close(); } void ConfigDialog::reject() { close(); } void ConfigDialog::keyPressEvent(QKeyEvent *e) { // Only act on Esc if we are instant-apply (in which case there will be no button box)... if (!buttonBox && Qt::Key_Escape==e->key() && Qt::NoModifier==e->modifiers()) { close(); } } void ConfigDialog::hideEvent(QHideEvent *e) { OSXStyle::self()->removeWindow(this); QMainWindow::hideEvent(e); } void ConfigDialog::closeEvent(QCloseEvent *e) { OSXStyle::self()->removeWindow(this); QMainWindow::closeEvent(e); } static bool isFocusable(QWidget *w) { if (/*qobject_cast(w) || qobject_cast(w) ||*/ qobject_cast(w) || qobject_cast(w) || qobject_cast(w)) { if (w->isVisible() && w->isEnabled()) { return true; } } return false; } static QWidget *firstFocusableWidget(QWidget *w) { if (!w) { return 0; } if (isFocusable(w)) { return w; } QObjectList children=w->children(); // Try each child first... for (QObject *c: children) { if (qobject_cast(c)) { QWidget *cw=static_cast(c); if (isFocusable(cw)) { return cw; } } } // Now try grandchildren... for (QObject *c: children) { if (qobject_cast(c)) { QWidget *f=firstFocusableWidget(static_cast(c)); if (f) { return f; } } } return 0; } void ConfigDialog::showEvent(QShowEvent *e) { if (!shown) { QTimer::singleShot(0, this, SLOT(setFocus())); shown=true; ensurePolished(); QDesktopWidget *dw=QApplication::desktop(); if (dw) { move(QPoint((dw->availableGeometry(this).size().width()-width())/2, 86)); } } OSXStyle::self()->addWindow(this); QMainWindow::showEvent(e); if (parentWidget()) { move(pos().x(), parentWidget()->pos().y()+16); } } #endif void ConfigDialog::setFocus() { #ifdef __APPLE__ // When the pref dialog is shown, no widget has focus - and there is a small (4px x 4px?) upper left // corner of a focus highlight drawn in the upper left of the dialog. // To work-around this, after the dialog is shown, look for a widget that can be set to have focus... QWidget *w=firstFocusableWidget(stack->currentWidget()); if (w) { w->setFocus(); } #endif } void ConfigDialog::macButtonPressed(QAbstractButton *b) { #ifdef __APPLE__ if (!buttonBox) { return; } if (b==buttonBox->button(QDialogButtonBox::Ok)) { slotButtonClicked(Dialog::Ok); } else if (b==buttonBox->button(QDialogButtonBox::Apply)) { slotButtonClicked(Dialog::Apply); } else if (b==buttonBox->button(QDialogButtonBox::Cancel)) { slotButtonClicked(Dialog::Cancel); } #else Q_UNUSED(b) #endif } #include "moc_configdialog.cpp"