File indexing completed on 2024-05-12 16:40:57
0001 /* This file is part of the KDE project 0002 Copyright (C) 2012 Oleg Kukharchuk <oleg.kuh@gmail.com> 0003 0004 This program is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This program is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this program; see the file COPYING. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 * Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "kexislider.h" 0021 0022 #include <QBoxLayout> 0023 #include <QSpinBox> 0024 #include <QPainter> 0025 #include <QStyle> 0026 #include <QSlider> 0027 #include <QStyleOptionSlider> 0028 0029 0030 class Q_DECL_HIDDEN KexiSlider::Private 0031 { 0032 public: 0033 Private() {} 0034 0035 Slider *slider; 0036 QSpinBox *spinBox; 0037 QBoxLayout *layout; 0038 }; 0039 0040 0041 class Slider : public QSlider 0042 { 0043 Q_OBJECT 0044 public: 0045 explicit Slider(QWidget *parent) : 0046 QSlider(parent) 0047 { 0048 0049 } 0050 0051 QSize sizeHint() const override 0052 { 0053 if (tickPosition() == QSlider::NoTicks) 0054 return QSlider::sizeHint(); 0055 0056 // preserve space for labels 0057 QSize extra(0,0); 0058 0059 QFontMetrics fm(font()); 0060 0061 int h = fm.height() + 3; 0062 int w = fm.width(QString::number(maximum())) + 3; 0063 0064 if (orientation() == Qt::Horizontal) { 0065 extra.setHeight(tickPosition() == QSlider::TicksBothSides ? h * 2 : h); 0066 } else { 0067 extra.setWidth(tickPosition() == QSlider::TicksBothSides ? w * 2 : w); 0068 } 0069 0070 return QSlider::sizeHint() + extra; 0071 } 0072 0073 protected: 0074 virtual void paintEvent(QPaintEvent *ev) override 0075 { 0076 if (tickPosition() == QSlider::NoTicks) 0077 return QSlider::paintEvent(ev); 0078 0079 QPainter p(this); 0080 QStyleOptionSlider option; 0081 initStyleOption(&option); 0082 0083 const QSlider::TickPosition ticks( option.tickPosition ); 0084 const int available(style()->proxy()->pixelMetric(QStyle::PM_SliderSpaceAvailable, &option, this)); 0085 int interval = option.tickInterval; 0086 if( interval < 1 ) interval = option.pageStep; 0087 if( interval < 1 ) return; 0088 0089 const QRect r(option.rect); 0090 const QPalette palette(option.palette); 0091 const int fudge(style()->proxy()->pixelMetric(QStyle::PM_SliderLength, &option, this) / 2); 0092 int current(option.minimum); 0093 int nextLabel = current; 0094 0095 const QFontMetrics fm(fontMetrics()); 0096 int h = fm.height() + 3; 0097 int w = fm.width(QString::number(option.maximum)) + 3; 0098 0099 if(available<w) 0100 nextLabel = -1; 0101 0102 qreal i = qreal(available) / qreal(orientation() == Qt::Horizontal ? w : h); 0103 qreal t = qreal(option.maximum)/qreal(interval); 0104 int valStep = t/ i + 1; 0105 0106 // Since there is no subrect for tickmarks do a translation here. 0107 p.save(); 0108 p.translate(r.x(), r.y()); 0109 0110 p.setPen(palette.color(QPalette::WindowText)); 0111 int extra = (option.tickPosition == QSlider::TicksBothSides ? 2 : 1); 0112 int tickSize(option.orientation == Qt::Horizontal ? (r.height() - h*extra)/3:(r.width() - w*extra)/3); 0113 0114 while(current <= option.maximum) 0115 { 0116 0117 const int position(QStyle::sliderPositionFromValue(option.minimum, option.maximum, 0118 current, available, option.upsideDown) + fudge); 0119 0120 // calculate positions 0121 if(option.orientation == Qt::Horizontal) 0122 { 0123 0124 if (ticks & QSlider::TicksAbove) { 0125 p.drawLine(position, h, position, tickSize + h); 0126 if(current == nextLabel) 0127 p.drawText(QRect(position - w/2, 0, w, h), Qt::AlignHCenter, QString::number(current)); 0128 } 0129 if (ticks & QSlider::TicksBelow) { 0130 p.drawLine( position, r.height() - h - tickSize, position, r.height() - h ); 0131 if(current == nextLabel) 0132 p.drawText(QRect(position - w/2, r.height() - h + 3, w, h), Qt::AlignHCenter, QString::number(current)); 0133 } 0134 } else { 0135 if (ticks & QSlider::TicksAbove) { 0136 p.drawLine(w, position, tickSize + w, position); 0137 if(current == nextLabel) 0138 p.drawText(QRect(0, position - h/2, w - 3, h), Qt::AlignRight | Qt::AlignVCenter, QString::number(current)); 0139 } 0140 if (ticks & QSlider::TicksBelow) { 0141 p.drawLine(r.width() - w - tickSize, position, r.width() - w, position ); 0142 if(current == nextLabel) 0143 p.drawText(QRect(r.width() - w + 3, position - h/2, w, h), Qt::AlignVCenter, QString::number(current)); 0144 } 0145 } 0146 0147 // go to next position 0148 if (current == nextLabel) 0149 nextLabel += interval*valStep; 0150 current += interval; 0151 0152 } 0153 p.restore(); 0154 option.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle; 0155 0156 style()->proxy()->drawComplexControl(QStyle::CC_Slider, &option, &p, this); 0157 } 0158 }; 0159 0160 KexiSlider::KexiSlider(QWidget *parent) 0161 : QWidget(parent) 0162 , d(new Private) 0163 { 0164 init(Qt::Horizontal); 0165 } 0166 0167 KexiSlider::KexiSlider(Qt::Orientation orientation, QWidget *parent) 0168 : QWidget(parent) 0169 , d(new Private) 0170 { 0171 init(orientation); 0172 } 0173 0174 void KexiSlider::init(Qt::Orientation orientation) 0175 { 0176 d->layout=new QBoxLayout(QBoxLayout::LeftToRight, this); 0177 d->layout->setSpacing(2); 0178 d->layout->setMargin(0); 0179 d->slider = new Slider(this); 0180 d->spinBox = new QSpinBox(this); 0181 d->spinBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 0182 0183 d->layout->addWidget(d->spinBox,0, Qt::AlignVCenter); 0184 d->layout->addWidget(d->slider,0, Qt::AlignVCenter); 0185 0186 connect(d->slider, SIGNAL(valueChanged(int)), this, SIGNAL(valueChanged(int))); 0187 connect(d->slider, SIGNAL(sliderPressed()), this, SIGNAL(sliderPressed())); 0188 connect(d->slider, SIGNAL(sliderReleased()), this, SIGNAL(sliderReleased())); 0189 connect(d->slider, SIGNAL(valueChanged(int)), d->spinBox, SLOT(setValue(int))); 0190 connect(d->spinBox, SIGNAL(valueChanged(int)), d->slider, SLOT(setValue(int))); 0191 0192 setMaximum(100); 0193 setOrientation(orientation); 0194 setTickPosition(QSlider::TicksAbove); 0195 } 0196 0197 KexiSlider::~KexiSlider() 0198 { 0199 delete d; 0200 } 0201 0202 void KexiSlider::setMinimum(int min) 0203 { 0204 d->spinBox->setMinimum(min); 0205 d->slider->setMinimum(min); 0206 } 0207 0208 void KexiSlider::setMaximum(int max) 0209 { 0210 d->spinBox->setMaximum(max); 0211 d->slider->setMaximum(max); 0212 } 0213 0214 void KexiSlider::setValue(int val) 0215 { 0216 d->slider->setValue(val); 0217 } 0218 0219 int KexiSlider::minimum() const 0220 { 0221 return d->slider->minimum(); 0222 } 0223 0224 int KexiSlider::maximum() const 0225 { 0226 return d->slider->maximum(); 0227 } 0228 0229 int KexiSlider::value() const 0230 { 0231 return d->slider->value(); 0232 } 0233 0234 void KexiSlider::setPageStep(int step) 0235 { 0236 d->slider->setPageStep(step); 0237 } 0238 0239 int KexiSlider::pageStep() const 0240 { 0241 return d->slider->pageStep(); 0242 } 0243 0244 void KexiSlider::setSingleStep(int step) 0245 { 0246 d->spinBox->setSingleStep(step); 0247 d->slider->setSingleStep(step); 0248 } 0249 0250 int KexiSlider::singleStep() const 0251 { 0252 return d->slider->singleStep(); 0253 } 0254 0255 void KexiSlider::setOrientation(Qt::Orientation o) 0256 { 0257 d->layout->removeWidget(d->spinBox); 0258 d->slider->setOrientation(o); 0259 if(o == Qt::Horizontal) 0260 d->layout->insertWidget(0, d->spinBox); 0261 else 0262 d->layout->addWidget(d->spinBox); 0263 updateLayout(); 0264 } 0265 0266 Qt::Orientation KexiSlider::orientation() const 0267 { 0268 return d->slider->orientation(); 0269 } 0270 0271 void KexiSlider::setTickInterval(int ti) 0272 { 0273 d->slider->setTickInterval(ti); 0274 } 0275 0276 int KexiSlider::tickInterval() const 0277 { 0278 return d->slider->tickInterval(); 0279 } 0280 0281 void KexiSlider::setTickPosition(QSlider::TickPosition pos) 0282 { 0283 d->slider->setTickPosition(pos); 0284 updateLayout(); 0285 } 0286 0287 QSlider::TickPosition KexiSlider::tickPosition() const 0288 { 0289 return d->slider->tickPosition(); 0290 } 0291 0292 void KexiSlider::setShowEditor(bool show) 0293 { 0294 d->spinBox->setVisible(show); 0295 0296 } 0297 0298 bool KexiSlider::showEditor() const 0299 { 0300 return d->spinBox->isVisible(); 0301 } 0302 0303 void KexiSlider::updateLayout() 0304 { 0305 d->layout->setDirection(orientation() == Qt::Horizontal ? 0306 QBoxLayout::LeftToRight : QBoxLayout::TopToBottom); 0307 0308 if (tickPosition() == QSlider::TicksBothSides 0309 || tickPosition() == QSlider::NoTicks) { 0310 d->layout->setAlignment(d->slider, orientation() == Qt::Horizontal ? 0311 Qt::AlignVCenter :Qt::AlignHCenter); 0312 d->layout->setAlignment(d->spinBox, orientation() == Qt::Horizontal ? 0313 Qt::AlignVCenter :Qt::AlignHCenter); 0314 } else { 0315 if (orientation() == Qt::Horizontal) { 0316 d->layout->setAlignment(d->slider, 0317 tickPosition() == QSlider::TicksAbove ? Qt::AlignBottom : Qt::AlignTop); 0318 d->layout->setAlignment(d->spinBox, 0319 tickPosition() == QSlider::TicksAbove ? Qt::AlignBottom : Qt::AlignTop); 0320 } else { 0321 d->layout->setAlignment(d->slider, 0322 tickPosition() == QSlider::TicksLeft ? Qt::AlignRight : Qt::AlignLeft); 0323 d->layout->setAlignment(d->spinBox, 0324 tickPosition() == QSlider::TicksLeft ? Qt::AlignRight : Qt::AlignLeft); 0325 } 0326 } 0327 } 0328 0329 #include "kexislider.moc"