diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 05f7bfa8..42af0d30 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 ) diff --git a/src/FrameworkWidgetFactory.cpp b/src/FrameworkWidgetFactory.cpp index 842ea00a..d3eaf3df 100644 --- a/src/FrameworkWidgetFactory.cpp +++ b/src/FrameworkWidgetFactory.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 #else # include "quick/FrameQuick_p.h" diff --git a/src/private/DropIndicatorOverlayInterface_p.h b/src/private/DropIndicatorOverlayInterface_p.h index c8ddf082..d28d86ee 100644 --- a/src/private/DropIndicatorOverlayInterface_p.h +++ b/src/private/DropIndicatorOverlayInterface_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 m_windowBeingDragged; diff --git a/src/private/indicators/SegmentedIndicators.cpp b/src/private/indicators/SegmentedIndicators.cpp new file mode 100644 index 00000000..646923de --- /dev/null +++ b/src/private/indicators/SegmentedIndicators.cpp @@ -0,0 +1,179 @@ +/* + This file is part of KDDockWidgets. + + SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company + Author: Sérgio Martins + + SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only + + Contact KDAB at for commercial licensing options. +*/ + +#include "SegmentedIndicators_p.h" +#include "DropArea_p.h" + +#include +#include + +#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 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 leftPoints = { topLeft, bottomLeft, + QPoint(left, bottom) + QPoint(l, -l), + topLeft + QPoint(l, l), topLeft + }; + + const QVector rightPoints = { topRight, bottomRight, + bottomRight + QPoint(-l, -l), + topRight + QPoint(-l, l) + }; + + const QVector topPoints = { topLeft, topRight, + topRight + QPoint(-l, l), + topLeft + QPoint(l, l) + }; + + const QVector bottomPoints = { bottomLeft, bottomRight, + bottomRight + QPoint(-l, -l), + bottomLeft + QPoint(l, -l) + }; + + { + + QPolygon bounds = QVector { 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 { { 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); +} diff --git a/src/private/indicators/SegmentedIndicators_p.h b/src/private/indicators/SegmentedIndicators_p.h new file mode 100644 index 00000000..5069f810 --- /dev/null +++ b/src/private/indicators/SegmentedIndicators_p.h @@ -0,0 +1,47 @@ +/* + This file is part of KDDockWidgets. + + SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company + Author: Sérgio Martins + + SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only + + Contact KDAB at for commercial licensing options. +*/ + +#ifndef KD_SEGMENTED_INDICATORS_P_H +#define KD_SEGMENTED_INDICATORS_P_H + +#include "DropIndicatorOverlayInterface_p.h" + +#include +#include + +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 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 m_segments; +}; + +} + +#endif