Add a Segmented Indicator
Just a POC of a new indicator style
This commit is contained in:
@@ -103,6 +103,7 @@ else()
|
||||
private/widgets/TitleBarWidget.cpp
|
||||
private/widgets/DockWidget.cpp
|
||||
private/widgets/QWidgetAdapter_widgets.cpp
|
||||
private/indicators/SegmentedIndicators.cpp
|
||||
# private/indicators/AnimatedIndicators.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
# include "widgets/TabWidgetWidget_p.h"
|
||||
# include "multisplitter/Separator_qwidget.h"
|
||||
# include "widgets/FloatingWindowWidget_p.h"
|
||||
# include "indicators/SegmentedIndicators_p.h"
|
||||
# include <QRubberBand>
|
||||
#else
|
||||
# include "quick/FrameQuick_p.h"
|
||||
|
||||
@@ -31,7 +31,8 @@ public:
|
||||
enum class Type {
|
||||
None = 0,
|
||||
Classic = 1,
|
||||
Animated = 2
|
||||
Segmented = 2,
|
||||
// Animated = 3
|
||||
};
|
||||
Q_ENUM(Type)
|
||||
|
||||
@@ -45,7 +46,10 @@ public:
|
||||
DropLocation_OutterLeft,
|
||||
DropLocation_OutterTop,
|
||||
DropLocation_OutterRight,
|
||||
DropLocation_OutterBottom
|
||||
DropLocation_OutterBottom,
|
||||
|
||||
DropLocation_First = DropLocation_Left,
|
||||
DropLocation_Last = DropLocation_OutterBottom,
|
||||
};
|
||||
Q_ENUM(DropLocation)
|
||||
|
||||
@@ -61,7 +65,7 @@ public:
|
||||
virtual Type indicatorType() const = 0;
|
||||
void hover(QPoint globalPos);
|
||||
|
||||
virtual QPoint posForIndicator(DropLocation) const = 0; // Used by unit-tests only
|
||||
virtual QPoint posForIndicator(DropLocation) const { return {}; }; // Used by unit-tests only
|
||||
|
||||
static KDDockWidgets::Location multisplitterLocationFor(DropLocation);
|
||||
|
||||
@@ -79,7 +83,7 @@ private:
|
||||
protected:
|
||||
virtual void hover_impl(QPoint globalPos) = 0;
|
||||
virtual void onHoveredFrameChanged(Frame *);
|
||||
virtual void updateVisibility() = 0;
|
||||
virtual void updateVisibility() {};
|
||||
|
||||
Frame *m_hoveredFrame = nullptr;
|
||||
QPointer<const FloatingWindow> m_windowBeingDragged;
|
||||
|
||||
179
src/private/indicators/SegmentedIndicators.cpp
Normal file
179
src/private/indicators/SegmentedIndicators.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
||||
*/
|
||||
|
||||
#include "SegmentedIndicators_p.h"
|
||||
#include "DropArea_p.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
|
||||
#define SEGMENT_GIRTH 40
|
||||
#define SEGMENT_PEN_WIDTH 4
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
|
||||
|
||||
SegmentedIndicators::SegmentedIndicators(DropArea *dropArea)
|
||||
: DropIndicatorOverlayInterface(dropArea)
|
||||
{
|
||||
}
|
||||
|
||||
SegmentedIndicators::~SegmentedIndicators()
|
||||
{
|
||||
}
|
||||
|
||||
DropIndicatorOverlayInterface::Type SegmentedIndicators::indicatorType() const
|
||||
{
|
||||
return Type::Segmented;
|
||||
}
|
||||
|
||||
void SegmentedIndicators::hover_impl(QPoint pt)
|
||||
{
|
||||
m_hoveredPt = mapFromGlobal(pt);
|
||||
updateSegments();
|
||||
setCurrentDropLocation(dropLocationForPos(m_hoveredPt));
|
||||
}
|
||||
|
||||
DropIndicatorOverlayInterface::DropLocation SegmentedIndicators::dropLocationForPos(QPoint pos) const
|
||||
{
|
||||
for (auto it = m_segments.cbegin(), end = m_segments.cend(); it != end; ++it) {
|
||||
if (it.value().containsPoint(pos, Qt::OddEvenFill)) {
|
||||
return it.key();
|
||||
}
|
||||
}
|
||||
|
||||
return DropLocation_None;
|
||||
}
|
||||
|
||||
void SegmentedIndicators::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
drawSegments(&p);
|
||||
}
|
||||
|
||||
QVector<QPolygon> SegmentedIndicators::segmentsForRect(QRect r, QPolygon ¢er, bool useOffset) const
|
||||
{
|
||||
const int penWidth = SEGMENT_PEN_WIDTH;
|
||||
const int halfPenWidth = penWidth / 2;
|
||||
|
||||
const int l = SEGMENT_GIRTH;
|
||||
const int top = (r.y() == 0 && useOffset) ? l : r.y();
|
||||
const int left = (r.x() == 0 && useOffset) ? l : r.x();
|
||||
const int right = (rect().right() == r.right() && useOffset) ? r.right() - l : r.right();
|
||||
const int bottom = (rect().bottom() == r.bottom() && useOffset) ? r.bottom() - l : r.bottom();
|
||||
const QPoint topLeft = { left + halfPenWidth, top + halfPenWidth };
|
||||
const QPoint topRight = { right, top + halfPenWidth };
|
||||
const QPoint bottomLeft = { left + halfPenWidth, bottom };
|
||||
const QPoint bottomRight = { right, bottom };
|
||||
|
||||
const QVector<QPoint> leftPoints = { topLeft, bottomLeft,
|
||||
QPoint(left, bottom) + QPoint(l, -l),
|
||||
topLeft + QPoint(l, l), topLeft
|
||||
};
|
||||
|
||||
const QVector<QPoint> rightPoints = { topRight, bottomRight,
|
||||
bottomRight + QPoint(-l, -l),
|
||||
topRight + QPoint(-l, l)
|
||||
};
|
||||
|
||||
const QVector<QPoint> topPoints = { topLeft, topRight,
|
||||
topRight + QPoint(-l, l),
|
||||
topLeft + QPoint(l, l)
|
||||
};
|
||||
|
||||
const QVector<QPoint> bottomPoints = { bottomLeft, bottomRight,
|
||||
bottomRight + QPoint(-l, -l),
|
||||
bottomLeft + QPoint(l, -l)
|
||||
};
|
||||
|
||||
{
|
||||
|
||||
QPolygon bounds = QVector<QPoint> { topLeft + QPoint(l, l),
|
||||
topRight + QPoint(-l, l),
|
||||
bottomRight + QPoint(-l, -l),
|
||||
bottomLeft + QPoint(l, -l)
|
||||
};
|
||||
const int maxWidth = bounds.boundingRect().width();
|
||||
const QPoint centerPos = bounds.boundingRect().center();
|
||||
|
||||
// Build the center
|
||||
const int indicatorWidth = qMin(300, maxWidth - 100);
|
||||
const int indicatorHeight = qMin(160, int(indicatorWidth * 0.60));
|
||||
const int tabWidth = int(indicatorWidth * 0.267);
|
||||
const int tabHeight = int(indicatorHeight * 0.187);
|
||||
const int centerRectLeft = centerPos.x() - indicatorWidth / 2;
|
||||
const int centerRectRight = centerPos.x() + indicatorWidth / 2;
|
||||
const int centerRectBottom = centerPos.y() + indicatorHeight / 2;
|
||||
const int centerRectTop = centerPos.y() - indicatorHeight / 2;
|
||||
|
||||
|
||||
center = QVector<QPoint> { { centerRectLeft, centerRectTop },
|
||||
{ centerRectLeft + tabWidth, centerRectTop },
|
||||
{ centerRectLeft + tabWidth, centerRectTop + tabHeight },
|
||||
{ centerRectRight, centerRectTop + tabHeight },
|
||||
{ centerRectRight, centerRectBottom },
|
||||
{ centerRectLeft, centerRectBottom },
|
||||
};
|
||||
}
|
||||
|
||||
return { leftPoints, topPoints, rightPoints, bottomPoints };
|
||||
}
|
||||
|
||||
void SegmentedIndicators::updateSegments()
|
||||
{
|
||||
m_segments.clear();
|
||||
|
||||
const bool hasMultipleFrames = m_dropArea->count() > 1;
|
||||
const bool needsInnerIndicators = hoveredFrameRect().isValid();
|
||||
const bool needsOutterIndicators = hasMultipleFrames || !needsInnerIndicators;
|
||||
QPolygon center;
|
||||
|
||||
if (needsInnerIndicators) {
|
||||
const bool useOffset = needsOutterIndicators;
|
||||
auto segments = segmentsForRect(hoveredFrameRect(), /*by-ref*/center, useOffset);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
m_segments.insert(DropLocation(DropLocation_Left + i), segments[i]);
|
||||
|
||||
m_segments.insert(DropLocation_Center, center);
|
||||
}
|
||||
|
||||
if (needsOutterIndicators) {
|
||||
auto segments = segmentsForRect(rect(), /*unused*/center);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
m_segments.insert(DropLocation(DropLocation_OutterLeft + i), segments[i]);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void SegmentedIndicators::drawSegments(QPainter *p)
|
||||
{
|
||||
for (int i = DropLocation_First; i <= DropLocation_Last; ++i)
|
||||
drawSegment(p, m_segments.value(DropLocation(i)));
|
||||
}
|
||||
|
||||
void SegmentedIndicators::drawSegment(QPainter *p, const QPolygon &segment)
|
||||
{
|
||||
if (segment.isEmpty())
|
||||
return;
|
||||
|
||||
QPen pen(Qt::black);
|
||||
pen.setWidth(SEGMENT_PEN_WIDTH);
|
||||
p->setPen(pen);
|
||||
QColor brush = QColor(0, 0xFF, 0xFF, 128);
|
||||
|
||||
if (segment.containsPoint(m_hoveredPt, Qt::OddEvenFill))
|
||||
brush = brush.darker(200);
|
||||
|
||||
p->setBrush(brush);
|
||||
p->drawPolygon(segment);
|
||||
}
|
||||
47
src/private/indicators/SegmentedIndicators_p.h
Normal file
47
src/private/indicators/SegmentedIndicators_p.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
||||
*/
|
||||
|
||||
#ifndef KD_SEGMENTED_INDICATORS_P_H
|
||||
#define KD_SEGMENTED_INDICATORS_P_H
|
||||
|
||||
#include "DropIndicatorOverlayInterface_p.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QPolygon>
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class SegmentedIndicators : public DropIndicatorOverlayInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SegmentedIndicators(DropArea *dropArea);
|
||||
~SegmentedIndicators() override;
|
||||
Type indicatorType() const override;
|
||||
void hover_impl(QPoint globalPos) override;
|
||||
|
||||
DropLocation dropLocationForPos(QPoint pos) const;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
|
||||
private:
|
||||
QVector<QPolygon> segmentsForRect(QRect, QPolygon ¢er, bool useOffset = false) const;
|
||||
void updateSegments();
|
||||
void drawSegments(QPainter *p);
|
||||
void drawSegment(QPainter *p, const QPolygon &segment);
|
||||
QPoint m_hoveredPt = {};
|
||||
QHash<DropLocation, QPolygon> m_segments;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user