Compare commits

..

56 Commits

Author SHA1 Message Date
mfreiholz
5dc8c616e9 updates build scripts: adds version number and removes no longer needed include paths. 2016-10-23 10:35:09 +02:00
mfreiholz
70e614b561 #18 adds flags to disable close functionality 2016-08-04 13:36:55 +02:00
mfreiholz
9e27d0b224 removes travis-ci icon as long as it doesn't work... 2016-07-15 09:06:33 +02:00
mfreiholz
6420381354 fix: adding a new SC without target SW to CenterDropArea causes invalid state 2016-07-15 08:43:49 +02:00
mfreiholz
0317c3ebeb fixes #20 crash on-show.
checks for _sections.count() > 0 in dropContent() and creates first
SectionWidget, if needed.
2016-06-08 06:50:08 +02:00
Manuel Freiholz
0be013bca2 Update .travis.yml 2016-06-01 13:39:50 +02:00
Manuel Freiholz
c27c0713f2 updates script for container-based build VMs 2016-06-01 13:37:08 +02:00
Manuel Freiholz
c07242cd5f Update .travis.yml 2016-06-01 12:53:27 +02:00
mfreiholz
65d1454c8c sets object name of container widget for tabs. 2016-05-23 08:25:52 +02:00
mfreiholz
4c65bde599 fix #2
makes tabs scrollable
scrollable by mouse wheel
adds menu to access not visible tabs
2016-05-23 07:47:28 +02:00
mfreiholz
5c9b383af8 Adds gitter batch 2016-04-27 17:59:07 +02:00
mfreiholz
f983a6ff85 Adds usage of ADS_Expects precondition check. 2016-04-21 09:16:45 +02:00
mfreiholz
3a5cd2118a Adds API method ContainerWidget::isSectionContentVisible.
Updates demo project with example.
2016-04-21 07:51:26 +02:00
mfreiholz
1fb78aa873 fix #16: resets WA_WState_Created to true. No longer apply flag to content wrapper widget.
note: setting this flag seems to be very ugly, since it breaks some special content views (3D stuff).
2016-04-21 07:05:27 +02:00
mfreiholz
c3e96cfa67 Fix #16: sets Qt::WA_WState_Created to false before reparenting to avoid internal hide->show sequence, which does forward mousemove events to splitter.. 2016-04-20 09:24:00 +02:00
mfreiholz
a1225545e4 Fixes compiler warnings. 2016-04-18 14:34:07 +02:00
mfreiholz
94b1fc85fc Use prefix for resources.
Updates demo project and README with clean main() and initStyleSheet().
2016-04-18 12:47:42 +02:00
mfreiholz
8c3a00d7b5 Create drop indicator images on the fly. No longer use PNG resources
(better in case of license issues). Drop images should be scalable now!
2016-04-18 12:30:24 +02:00
mfreiholz
e04b5c7900 fix: #13 custom drop area widgets
- adds public function to set widgets
- some cleanup changes
2016-04-18 09:40:51 +02:00
mfreiholz
180ed31fc4 Updates API for #13 - Custom drop indicators.
DropOverlayCross is better reusable now and area widgets can be changed.
2016-04-18 08:27:28 +02:00
mfreiholz
9c530dcc58 refactor: Renames member _splitAreas to _cross.
mod: removes ContainerWiget::set/Orientation() members. Obsolete since
everything is managed over drop areas.
2016-04-18 07:18:50 +02:00
mfreiholz
86c23c6269 refactor: Renames DropSplitAreas => DropOverlayCross
mod: Uses QPalette::Highlight color, instead of in-source fixed color, for area overlay painting.
2016-04-18 07:06:46 +02:00
mfreiholz
0bd5dc57fa Preparation for #13 - custom drop images.
- DropOverlay and DropSplitArea widgets are friendly, but still separate
  for better customization.
- Both classes are reusable, no longer create a new instance of
  DropSplitArea for each show call.
2016-04-15 12:14:50 +02:00
mfreiholz
5041f5076a fix #17 - get rid of static drop overlay
- holds one instance of DropOverlay for each ContainerWidget.
2016-04-15 08:21:23 +02:00
mfreiholz
823028c6aa clean handling of QEvent::accept() 2016-04-11 09:12:48 +02:00
mfreiholz
fffc84374a Use QT_VERSION_CHECK macro for better compatibility. 2016-04-11 07:27:02 +02:00
mfreiholz
14a80f1753 fix: FloatingWidget calls ContainerWidget::hideSectionContent(...) instead
of it's own close() method to be consistent in the way of hiding and
showing contents.
2016-04-11 07:15:42 +02:00
mfreiholz
932ec71ad8 Issue #15
- adds sectionContentVisibilityChanged(...) signal
2016-04-11 07:08:40 +02:00
mfreiholz
f2a352c305 fixes position mapping of sections inside container. all positions are now relative to it's container. 2016-04-06 07:52:17 +02:00
mfreiholz
0eea083f07 removes no longer required debug prints 2016-04-06 06:55:27 +02:00
mfreiholz
f110ad8ec7 fix: position (not yet tested) 2016-04-05 15:00:05 +02:00
mfreiholz
d8dc37b788 adds read() for SectionIndexData
updates unit-tests
2016-04-05 13:23:01 +02:00
mfreiholz
0105ee1de7 fixes file name letter case for builds on linux
adds travis-ci icon
2016-04-05 08:01:14 +02:00
mfreiholz
40727028d6 Fix version check for QLayout::replaceWidget() 2016-04-05 07:54:12 +02:00
mfreiholz
a8383d14d0 Updates travis build script 2016-04-05 07:45:12 +02:00
mfreiholz
1566782173 Removes TODOs from README, managed with GitHub Issues now.
Updates "Gettings started / Example" section.
2016-04-05 07:39:15 +02:00
mfreiholz
0d50bf8ac0 init x and y 2016-04-04 08:52:03 +02:00
mfreiholz
195b34c0ca Serialize SectionIndexData on storeSate(). 2016-04-04 08:51:38 +02:00
mfreiholz
ac80242545 Fixes version check for serialized data.
Use new ::serialization package for save- and restore state.
2016-04-01 13:06:12 +02:00
mfreiholz
2a1bc4c43b Refactor: Add *Entity to all serialization entities to omit name collisions 2016-04-01 12:35:33 +02:00
mfreiholz
34891e746e Refactor: Moves EntryTypes into namespace enum.
Adds ADS_EXPORT_API defines to public classes.
2016-04-01 12:11:52 +02:00
mfreiholz
158dac5309 Build
- Use absolute file paths by using $$PWD variable.
- Adds unit testing project + unit-test for serialization.
- Adds ADS_IMPORT define possibility for static code include.

Core
- Fixes usage of QBuffer.. missed the QBuffer::open() call..
2016-04-01 11:49:16 +02:00
mfreiholz
5ab1751f9b Build: Moves source file list into separate *.pri file. 2016-04-01 09:58:26 +02:00
mfreiholz
d29bdd1438 Fixes "const" usage 2016-04-01 09:57:52 +02:00
mfreiholz
3d9e3c2efc fixes qt4 compatibility 2016-04-01 09:53:30 +02:00
mfreiholz
4debaaaf85 Serialization
- Adds prototype impls for InMemoryWriter and InMemoryReader.
- Use major- and minor-version idiom
2016-04-01 09:52:59 +02:00
mfreiholz
6c587ff8c4 Adds prototype classes for better serialization. 2016-03-31 14:47:19 +02:00
mfreiholz
a566ea64be Merge branch 'tanaxiusi-master' 2016-03-16 14:00:46 +01:00
mfreiholz
45f269835b Using grabMouse() leads to more problems. I think there is a better way to ensure that the mouse sometimes doesn't lose the current dragging floating widget. 2016-03-16 13:58:57 +01:00
mfreiholz
4ce04ecf2b Merge branch 'master' of https://github.com/tanaxiusi/Qt-Advanced-Docking-System into tanaxiusi-master 2016-03-16 13:29:46 +01:00
mfreiholz
69dca97333 Updates notes. 2016-03-16 13:29:05 +01:00
tanaxiusi
f2f5de707c Fix bug after saving-restoring a empty container with no SectionWidget.
SectionTitleWidget::mousePressEvent call 'grabMouse' to ensure the floaing widget can always follow the mouse after floated.
SectionTitleWidget::mouseMoveEvent check Qt::LeftButton.
2016-03-16 20:12:39 +08:00
mfreiholz
d2d2467101 Adds possibility to remove section contents. 2016-03-14 15:12:34 +01:00
mfreiholz
9c958d0d22 Adds notes 2016-03-09 12:02:31 +01:00
mfreiholz
21f316577f Again... Fixes invisible FloatingWidget's title-widget after restoring state. 2016-03-09 08:37:20 +01:00
mfreiholz
afb5abd635 Fixes invisible FloatingWidget's title-widget after restoring state. 2016-03-09 08:07:16 +01:00
51 changed files with 2429 additions and 805 deletions

14
.astylerc Normal file
View File

@@ -0,0 +1,14 @@
--style=allman
--indent=force-tab=4
--align-pointer=type
--align-reference=type
--pad-oper
--pad-header
--unpad-paren
--remove-comment-prefix
--mode=c

View File

@@ -1,16 +1,24 @@
# Build with Qt5
language:
- cpp
compiler:
- g++
before_install:
- sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa
- sudo apt-get update -qq
- sudo apt-get install -qq qtbase5-dev qtdeclarative5-dev libqt5webkit5-dev libsqlite3-dev
- sudo apt-get install -qq qt5-default qttools5-dev-tools
addons:
apt:
sources:
- ubuntu-sdk-team
packages:
- qt5-qmake
- qtbase5-dev
- qtdeclarative5-dev
- libqt5webkit5-dev
- libsqlite3-dev
script:
- qmake -qt=qt5 -v
- qmake -qt=qt5 -r build.pro
- make
- make
#- sudo apt-get install -qq qtbase5-dev qtdeclarative5-dev libqt5webkit5-dev libsqlite3-dev
#- sudo apt-get install -qq qt5-default qttools5-dev-tools

View File

@@ -0,0 +1,24 @@
SOURCES += \
$$PWD/src/API.cpp \
$$PWD/src/ContainerWidget.cpp \
$$PWD/src/SectionWidget.cpp \
$$PWD/src/SectionContent.cpp \
$$PWD/src/SectionTitleWidget.cpp \
$$PWD/src/SectionContentWidget.cpp \
$$PWD/src/DropOverlay.cpp \
$$PWD/src/FloatingWidget.cpp \
$$PWD/src/Internal.cpp \
$$PWD/src/Serialization.cpp
HEADERS += \
$$PWD/include/ads/API.h \
$$PWD/include/ads/ContainerWidget.h \
$$PWD/include/ads/SectionWidget.h \
$$PWD/include/ads/SectionContent.h \
$$PWD/include/ads/SectionTitleWidget.h \
$$PWD/include/ads/SectionContentWidget.h \
$$PWD/include/ads/DropOverlay.h \
$$PWD/include/ads/FloatingWidget.h \
$$PWD/include/ads/Internal.h \
$$PWD/include/ads/Serialization.h

View File

@@ -1,25 +1,23 @@
TARGET = AdvancedDockingSystem
#VERSION = 0.1.0
TEMPLATE = lib
VERSION = 1.0.0
CONFIG += adsBuildShared
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TEMPLATE = lib
greaterThan(QT_MAJOR_VERSION, 4): DEFINES += ADS_NAMESPACE_ENABLED
adsBuildShared {
CONFIG += shared
DEFINES += ADS_EXPORT
CONFIG += shared
DEFINES += ADS_EXPORT
}
!adsBuildShared {
CONFIG += staticlib
CONFIG += staticlib
}
INCLUDEPATH += $$PWD/src
INCLUDEPATH += $$PWD/include
greaterThan(QT_MAJOR_VERSION, 4): DEFINES += ADS_NAMESPACE_ENABLED
windows {
# MinGW
*-g++* {
@@ -31,27 +29,7 @@ windows {
}
}
SOURCES += \
src/API.cpp \
src/ContainerWidget.cpp \
src/SectionWidget.cpp \
src/SectionContent.cpp \
src/SectionTitleWidget.cpp \
src/SectionContentWidget.cpp \
src/DropOverlay.cpp \
src/FloatingWidget.cpp \
src/Internal.cpp
HEADERS += \
include/ads/API.h \
include/ads/ContainerWidget.h \
include/ads/SectionWidget.h \
include/ads/SectionContent.h \
include/ads/SectionTitleWidget.h \
include/ads/SectionContentWidget.h \
include/ads/DropOverlay.h \
include/ads/FloatingWidget.h \
include/ads/Internal.h
RESOURCES += \
res/ads.qrc
include(AdvancedDockingSystem.pri)

View File

@@ -7,7 +7,9 @@ class QSplitter;
// DLL Export API
#ifdef _WIN32
#ifdef ADS_EXPORT
#if defined(ADS_IMPORT)
#define ADS_EXPORT_API
#elif defined(ADS_EXPORT)
#define ADS_EXPORT_API __declspec(dllexport)
#else
#define ADS_EXPORT_API __declspec(dllimport)
@@ -17,7 +19,7 @@ class QSplitter;
#endif
// Use namespace
// Disabled with Qt4!
// Disabled with Qt4, it makes problems with signals and slots.
#ifdef ADS_NAMESPACE_ENABLED
#define ADS_NAMESPACE_BEGIN namespace ads {
#define ADS_NAMESPACE_END }
@@ -28,9 +30,19 @@ class QSplitter;
#define ADS_NS
#endif
// Always enable "serialization" namespace.
// It is not required for signals and slots.
#define ADS_NAMESPACE_SER_BEGIN namespace ads { namespace serialization {
#define ADS_NAMESPACE_SER_END }}
#define ADS_NS_SER ::ads::serialization
// Width of the native window frame border (based on OS).
#define ADS_WINDOW_FRAME_BORDER_WIDTH 7
// Beautiful C++ stuff.
#define ADS_Expects(cond)
#define ADS_Ensures(cond)
// Indicates whether ADS should include animations.
//#define ADS_ANIMATIONS_ENABLED 1
//#define ADS_ANIMATION_DURATION 150

View File

@@ -14,9 +14,11 @@ class QGridLayout;
#include "ads/Internal.h"
#include "ads/SectionContent.h"
#include "ads/FloatingWidget.h"
#include "ads/Serialization.h"
ADS_NAMESPACE_BEGIN
class SectionWidget;
class DropOverlay;
class InternalContentData;
@@ -27,7 +29,6 @@ class InternalContentData;
class ADS_EXPORT_API ContainerWidget : public QFrame
{
Q_OBJECT
Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged)
friend class SectionContent;
friend class SectionWidget;
@@ -43,9 +44,6 @@ public:
// Public API
//
Qt::Orientation orientation() const;
void setOrientation(Qt::Orientation orientation);
/*!
* Adds the section-content <em>sc</em> to this container-widget into the section-widget <em>sw</em>.
* If <em>sw</em> is not NULL, the <em>area</em> is used to indicate how the content should be arranged.
@@ -54,6 +52,13 @@ public:
*/
SectionWidget* addSectionContent(const SectionContent::RefPtr& sc, SectionWidget* sw = NULL, DropArea area = CenterDropArea);
/*!
* Completely removes the <em>sc</em> from this ContainerWidget.
* This container will no longer hold a reference to the content.
* The content can be safely deleted.
*/
bool removeSectionContent(const SectionContent::RefPtr& sc);
/*!
* Shows the specific SectionContent in UI.
* Independed of the current state, whether it is used inside a section or is floating.
@@ -72,6 +77,11 @@ public:
*/
bool raiseSectionContent(const SectionContent::RefPtr& sc);
/*!
* Indicates whether the SectionContent <em>sc</em> is visible.
*/
bool isSectionContentVisible(const SectionContent::RefPtr& sc);
/*!
* Creates a QMenu based on available SectionContents.
* The caller is responsible to delete the menu.
@@ -101,6 +111,14 @@ public:
QRect outerBottomDropRect() const;
QRect outerLeftDropRect() const;
/*!
* \brief contents
* \return List of known SectionContent for this ContainerWidget.
*/
QList<SectionContent::RefPtr> contents() const;
QPointer<DropOverlay> dropOverlay() const;
private:
//
// Internal Stuff Begins Here
@@ -113,8 +131,13 @@ private:
SectionWidget* dropContentOuterHelper(QLayout* l, const InternalContentData& data, Qt::Orientation orientation, bool append);
// Serialization
QByteArray saveHierarchy() const;
void saveFloatingWidgets(QDataStream& out) const;
void saveSectionWidgets(QDataStream& out, QWidget* widget) const;
bool saveSectionIndex(ADS_NS_SER::SectionIndexData &sid) const;
bool restoreHierarchy(const QByteArray& data);
bool restoreFloatingWidgets(QDataStream& in, int version, QList<FloatingWidget*>& floatings);
bool restoreSectionWidgets(QDataStream& in, int version, QSplitter* currentSplitter, QList<SectionWidget*>& sections, QList<SectionContent::RefPtr>& contentsToHide);
@@ -134,6 +157,13 @@ signals:
*/
void activeTabChanged(const SectionContent::RefPtr& sc, bool active);
/*!
* Emits whenever the visibility of a SectionContent changes.
* \see showSectionContent(), hideSectionContent()
* \since 0.2
*/
void sectionContentVisibilityChanged(const SectionContent::RefPtr& sc, bool visible);
private:
// Elements inside container.
QList<SectionWidget*> _sections;
@@ -152,6 +182,9 @@ private:
Qt::Orientation _orientation;
QPointer<QSplitter> _splitter; // $mfreiholz: I'd like to remove this variable entirely,
// because it changes during user interaction anyway.
// Drop overlay stuff.
QPointer<DropOverlay> _dropOverlay;
};
ADS_NAMESPACE_END

View File

@@ -1,66 +1,86 @@
#ifndef DROP_OVERLAY_H
#define DROP_OVERLAY_H
#include <QPointer>
#include <QHash>
#include <QRect>
#include <QFrame>
class QGridLayout;
#include "ads/API.h"
ADS_NAMESPACE_BEGIN
class DropSplitAreas;
class DropOverlayCross;
// DropOverlay paints a translucent rectangle over another widget.
// It can also show different types of drop area indicators.
class DropOverlay : public QFrame
/*!
* DropOverlay paints a translucent rectangle over another widget. The geometry
* of the rectangle is based on the mouse location.
*/
class ADS_EXPORT_API DropOverlay : public QFrame
{
Q_OBJECT
friend class DropOverlayCross;
public:
DropOverlay(DropAreas areas, QWidget* parent);
DropOverlay(QWidget* parent);
virtual ~DropOverlay();
void setFullAreaDropEnabled(bool enabled) { _fullAreaDrop = enabled; }
void setAllowedAreas(DropAreas areas);
DropAreas allowedAreas() const;
void setAreaWidgets(const QHash<DropArea, QWidget*>& widgets);
DropArea cursorLocation() const;
DropArea showDropOverlay(QWidget* target);
void showDropOverlay(QWidget* target, const QRect& targetAreaRect);
void hideDropOverlay();
protected:
virtual void paintEvent(QPaintEvent *e);
virtual void showEvent(QShowEvent* e);
virtual void hideEvent(QHideEvent* e);
virtual void resizeEvent(QResizeEvent* e);
virtual void moveEvent(QMoveEvent* e);
private:
DropSplitAreas* _splitAreas;
DropAreas _allowedAreas;
DropOverlayCross* _cross;
bool _fullAreaDrop;
QPointer<QWidget> _target;
QRect _targetRect;
DropArea _lastLocation;
};
// AbstractDropAreas is used as base for drop area indicator widgets.
class AbstractDropAreas : public QWidget
{
public:
AbstractDropAreas(QWidget* parent) : QWidget(parent) {}
virtual DropArea cursorLocation() const = 0;
};
// DropSplitAreas shows a cross with 5 different drop area possibilities.
class DropSplitAreas : public AbstractDropAreas
/*!
* DropOverlayCross shows a cross with 5 different drop area possibilities.
* I could have handled everything inside DropOverlay, but because of some
* styling issues it's better to have a separate class for the cross.
*/
class DropOverlayCross : public QWidget
{
Q_OBJECT
friend class DropOverlay;
public:
DropSplitAreas(DropAreas areas, QWidget* parent);
virtual DropArea cursorLocation() const;
DropOverlayCross(DropOverlay* overlay);
virtual ~DropOverlayCross();
void setAreaWidgets(const QHash<DropArea, QWidget*>& widgets);
DropArea cursorLocation() const;
protected:
virtual void showEvent(QShowEvent* e);
private:
QWidget* _top;
QWidget* _right;
QWidget* _bottom;
QWidget* _left;
QWidget* _center;
void reset();
private:
DropOverlay* _overlay;
QHash<DropArea, QWidget*> _widgets;
QGridLayout* _grid;
};
DropArea showDropOverlay(QWidget* parent, DropAreas areas = AllAreas);
void showDropOverlay(QWidget* parent, const QRect& areaRect, DropAreas areas = AllAreas);
void hideDropOverlay();
ADS_NAMESPACE_END
#endif

View File

@@ -30,6 +30,9 @@ public:
public://private:
bool takeContent(InternalContentData& data);
private slots:
void onCloseButtonClicked();
private:
ContainerWidget* _container;
SectionContent::RefPtr _content;

View File

@@ -6,7 +6,7 @@
#include "ads/API.h"
#if QT_VERSION >= 0x050000
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#else
#include "ads/SectionContent.h"
#endif

View File

@@ -24,6 +24,14 @@ public:
typedef QSharedPointer<SectionContent> RefPtr;
typedef QWeakPointer<SectionContent> WeakPtr;
enum Flag
{
None = 0,
Closeable = 1,
AllFlags = Closeable
};
Q_DECLARE_FLAGS(Flags, Flag)
/*!
* Creates new content, associates it to <em>container</em> and takes ownership of
* <em>title</em>- and <em>content</em>- widgets.
@@ -41,10 +49,12 @@ public:
ContainerWidget* containerWidget() const;
QWidget* titleWidget() const;
QWidget* contentWidget() const;
Flags flags() const;
QString visibleTitle() const;
QString title() const;
void setTitle(const QString& title);
void setFlags(const Flags f);
private:
const int _uid;
@@ -56,7 +66,11 @@ private:
// Optional attributes
QString _title;
Flags _flags;
/* Note: This method could be a problem in static build environment
* since it may begin with 0 for every module which uses ADS.
*/
static int GetNextUid();
};

View File

@@ -5,8 +5,11 @@
#include <QPointer>
#include <QList>
#include <QFrame>
#include <QScrollArea>
class QBoxLayout;
class QStackedLayout;
class QPushButton;
class QMenu;
#include "ads/API.h"
#include "ads/Internal.h"
@@ -41,17 +44,24 @@ public:
void addContent(const InternalContentData& data, bool autoActivate);
bool takeContent(int uid, InternalContentData& data);
int indexOfContent(const SectionContent::RefPtr& c) const;
int indexOfContentByUid(int uid) const;
int indexOfContentByTitlePos(const QPoint& pos, QWidget* exclude = NULL) const;
int currentIndex() const;
void moveContent(int from, int to);
protected:
virtual void showEvent(QShowEvent*);
public slots:
void setCurrentIndex(int index);
private slots:
void onSectionTitleClicked();
void onCloseButtonClicked();
void onTabsMenuActionTriggered(bool);
void updateTabsMenu();
private:
const int _uid;
@@ -61,7 +71,14 @@ private:
QList<SectionTitleWidget*> _sectionTitles;
QList<SectionContentWidget*> _sectionContents;
QBoxLayout *_tabsLayout;
QBoxLayout* _topLayout;
QScrollArea* _tabsScrollArea;
QWidget* _tabsContainerWidget;
QBoxLayout* _tabsLayout;
QPushButton* _tabsMenuButton;
QPushButton* _closeButton;
int _tabsLayoutInitCount; // used for calculations on _tabsLayout modification calls.
QStackedLayout *_contentsLayout;
QPoint _mousePressPoint;
@@ -71,5 +88,16 @@ private:
static int GetNextUid();
};
/* Custom scrollable implementation for tabs */
class SectionWidgetTabsScrollArea : public QScrollArea
{
public:
SectionWidgetTabsScrollArea(SectionWidget* sectionWidget, QWidget* parent = NULL);
virtual ~SectionWidgetTabsScrollArea();
protected:
virtual void wheelEvent(QWheelEvent*);
};
ADS_NAMESPACE_END
#endif

View File

@@ -0,0 +1,165 @@
#ifndef ADS_SERIALIZATION_H
#define ADS_SERIALIZATION_H
#include <QtGlobal>
#include <QList>
#include <QString>
#include <QDataStream>
#include <QBuffer>
#include "ads/API.h"
ADS_NAMESPACE_SER_BEGIN
enum EntryType
{
ET_Unknown = 0x00000000,
ET_Hierarchy = 0x00000001,
ET_SectionIndex = 0x00000002,
// Begin of custom entry types (e.g. CustomType + 42)
ET_Custom = 0x0000ffff
};
class ADS_EXPORT_API HeaderEntity
{
public:
static qint32 MAGIC;
static qint32 MAJOR_VERSION;
static qint32 MINOR_VERSION;
HeaderEntity();
qint32 magic;
qint32 majorVersion;
qint32 minorVersion;
};
QDataStream& operator<<(QDataStream& out, const HeaderEntity& data);
QDataStream& operator>>(QDataStream& in, HeaderEntity& data);
class ADS_EXPORT_API OffsetsHeaderEntity
{
public:
OffsetsHeaderEntity();
qint64 entriesCount;
QList<class OffsetsHeaderEntryEntity> entries;
};
QDataStream& operator<<(QDataStream& out, const OffsetsHeaderEntity& data);
QDataStream& operator>>(QDataStream& in, OffsetsHeaderEntity& data);
class ADS_EXPORT_API OffsetsHeaderEntryEntity
{
public:
OffsetsHeaderEntryEntity();
qint32 type;
qint64 offset;
qint64 contentSize;
};
QDataStream& operator<<(QDataStream& out, const OffsetsHeaderEntryEntity& data);
QDataStream& operator>>(QDataStream& in, OffsetsHeaderEntryEntity& data);
class ADS_EXPORT_API SectionEntity
{
public:
SectionEntity();
qint32 x;
qint32 y;
qint32 width;
qint32 height;
qint32 currentIndex;
qint32 sectionContentsCount;
QList<class SectionContentEntity> sectionContents;
};
QDataStream& operator<<(QDataStream& out, const SectionEntity& data);
QDataStream& operator>>(QDataStream& in, SectionEntity& data);
class ADS_EXPORT_API SectionContentEntity
{
public:
SectionContentEntity();
QString uniqueName;
bool visible;
qint32 preferredIndex;
};
QDataStream& operator<<(QDataStream& out, const SectionContentEntity& data);
QDataStream& operator>>(QDataStream& in, SectionContentEntity& data);
class ADS_EXPORT_API FloatingContentEntity
{
public:
FloatingContentEntity();
QString uniqueName;
qint32 xpos;
qint32 ypos;
qint32 width;
qint32 height;
bool visible;
};
QDataStream& operator<<(QDataStream& out, const FloatingContentEntity& data);
QDataStream& operator>>(QDataStream& in, FloatingContentEntity& data);
// Type: OffsetHeaderEntry::Hierarchy
class ADS_EXPORT_API HierarchyData
{
public:
HierarchyData();
QByteArray data;
};
QDataStream& operator<<(QDataStream& out, const HierarchyData& data);
QDataStream& operator>>(QDataStream& in, HierarchyData& data);
// Type: OffsetHeaderEntry::SectionIndex
class ADS_EXPORT_API SectionIndexData
{
public:
SectionIndexData();
qint32 sectionsCount;
QList<SectionEntity> sections;
};
QDataStream& operator<<(QDataStream& out, const SectionIndexData& data);
QDataStream& operator>>(QDataStream& in, SectionIndexData& data);
/*!
* \brief The InMemoryWriter class writes into a QByteArray.
*/
class ADS_EXPORT_API InMemoryWriter
{
public:
InMemoryWriter();
bool write(qint32 entryType, const QByteArray& data);
bool write(const SectionIndexData& data);
QByteArray toByteArray() const;
qint32 offsetsCount() const { return _offsetsHeader.entriesCount; }
private:
QBuffer _contentBuffer;
OffsetsHeaderEntity _offsetsHeader;
};
/*!
* \brief The InMemoryReader class
*/
class ADS_EXPORT_API InMemoryReader
{
public:
InMemoryReader(const QByteArray& data);
bool initReadHeader();
bool read(qint32 entryType, QByteArray &data);
bool read(SectionIndexData& sid);
qint32 offsetsCount() const { return _offsetsHeader.entriesCount; }
private:
QByteArray _data;
OffsetsHeaderEntity _offsetsHeader;
};
ADS_NAMESPACE_SER_END
#endif

View File

@@ -1,17 +1,5 @@
<RCC>
<qresource prefix="/">
<file>img/dnd-thumbnail.png</file>
<file>img/dock-bottom.png</file>
<file>img/dock-center.png</file>
<file>img/dock-left.png</file>
<file>img/dock-right.png</file>
<file>img/dock-top.png</file>
<file>img/split-bottom.png</file>
<file>img/split-left.png</file>
<file>img/split-right.png</file>
<file>img/split-top.png</file>
<file>img/splitter-horizontal.png</file>
<file>img/splitter-vertical.png</file>
<qresource prefix="/ads">
<file>stylesheets/default-windows.css</file>
<file>stylesheets/vendor-partsolutions.css</file>
<file>stylesheets/modern-windows.css</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 516 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 647 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 619 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

View File

@@ -22,6 +22,12 @@ SectionWidget
border: 1px solid palette(light);
}
ads--SectionWidget #tabsMenuButton::menu-indicator,
SectionWidget #tabsMenuButton::menu-indicator
{
image: none;
}
ads--SectionTitleWidget,
SectionTitleWidget
{

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,5 @@
#include "ads/DropOverlay.h"
#include <QDebug>
#include <QPointer>
#include <QPaintEvent>
#include <QResizeEvent>
@@ -15,20 +14,114 @@ ADS_NAMESPACE_BEGIN
// Helper /////////////////////////////////////////////////////////////
static QWidget* createDropWidget(const QString& img)
static QPixmap createDropIndicatorPixmap(const QPalette& pal, const QSizeF& size, DropArea dropArea)
{
QLabel* label = new QLabel();
label->setObjectName("DropAreaLabel");
label->setPixmap(QPixmap(img));
return label;
const QColor borderColor = pal.color(QPalette::Active, QPalette::Highlight);
const QColor backgroundColor = pal.color(QPalette::Active, QPalette::Base);
const QColor areaBackgroundColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(150);
QPixmap pm(size.width(), size.height());
pm.fill(QColor(0, 0, 0, 0));
QPainter p(&pm);
QPen pen = p.pen();
QRectF baseRect(pm.rect());
// Fill
p.fillRect(baseRect, backgroundColor);
// Drop area rect.
if (true)
{
p.save();
QRectF areaRect;
QLineF areaLine;
QLinearGradient gradient;
switch (dropArea)
{
case TopDropArea:
areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width(), baseRect.height() * .5f);
areaLine = QLineF(areaRect.bottomLeft(), areaRect.bottomRight());
gradient.setStart(areaRect.topLeft());
gradient.setFinalStop(areaRect.bottomLeft());
gradient.setColorAt(0.f, areaBackgroundColor);
gradient.setColorAt(1.f, areaBackgroundColor.lighter(120));
break;
case RightDropArea:
areaRect = QRectF(baseRect.width() * .5f, baseRect.y(), baseRect.width() * .5f, baseRect.height());
areaLine = QLineF(areaRect.topLeft(), areaRect.bottomLeft());
gradient.setStart(areaRect.topLeft());
gradient.setFinalStop(areaRect.topRight());
gradient.setColorAt(0.f, areaBackgroundColor.lighter(120));
gradient.setColorAt(1.f, areaBackgroundColor);
break;
case BottomDropArea:
areaRect = QRectF(baseRect.x(), baseRect.height() * .5f, baseRect.width(), baseRect.height() * .5f);
areaLine = QLineF(areaRect.topLeft(), areaRect.topRight());
gradient.setStart(areaRect.topLeft());
gradient.setFinalStop(areaRect.bottomLeft());
gradient.setColorAt(0.f, areaBackgroundColor.lighter(120));
gradient.setColorAt(1.f, areaBackgroundColor);
break;
case LeftDropArea:
areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width() * .5f, baseRect.height());
areaLine = QLineF(areaRect.topRight(), areaRect.bottomRight());
gradient.setStart(areaRect.topLeft());
gradient.setFinalStop(areaRect.topRight());
gradient.setColorAt(0.f, areaBackgroundColor);
gradient.setColorAt(1.f, areaBackgroundColor.lighter(120));
break;
default:
break;
}
if (areaRect.isValid())
{
p.fillRect(areaRect, gradient);
pen = p.pen();
pen.setColor(borderColor);
pen.setStyle(Qt::DashLine);
p.setPen(pen);
p.drawLine(areaLine);
}
p.restore();
}
// Border
if (true)
{
p.save();
pen = p.pen();
pen.setColor(borderColor);
pen.setWidth(1);
p.setPen(pen);
p.drawRect(baseRect.adjusted(0, 0, -pen.width(), -pen.width()));
p.restore();
}
return pm;
}
static QWidget* createDropIndicatorWidget(DropArea dropArea)
{
QLabel* l = new QLabel();
l->setObjectName("DropAreaLabel");
const qreal metric = static_cast<qreal>(l->fontMetrics().height()) * 2.f;
const QSizeF size(metric, metric);
l->setPixmap(createDropIndicatorPixmap(l->palette(), size, dropArea));
return l;
}
///////////////////////////////////////////////////////////////////////
DropOverlay::DropOverlay(DropAreas areas, QWidget *parent) :
DropOverlay::DropOverlay(QWidget* parent) :
QFrame(parent),
_splitAreas(NULL),
_fullAreaDrop(false)
_allowedAreas(InvalidDropArea),
_cross(new DropOverlayCross(this)),
_fullAreaDrop(false),
_lastLocation(InvalidDropArea)
{
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
setWindowOpacity(0.2);
@@ -39,41 +132,122 @@ DropOverlay::DropOverlay(DropAreas areas, QWidget *parent) :
l->setSpacing(0);
setLayout(l);
_splitAreas = new DropSplitAreas(areas, parent);
_splitAreas->move(pos());
_splitAreas->resize(size());
_splitAreas->setVisible(true);
// Cross with default drop area widgets.
QHash<DropArea, QWidget*> areaWidgets;
areaWidgets.insert(ADS_NS::TopDropArea, createDropIndicatorWidget(TopDropArea)); //createDropWidget(":/img/split-top.png"));
areaWidgets.insert(ADS_NS::RightDropArea, createDropIndicatorWidget(RightDropArea));//createDropWidget(":/img/split-right.png"));
areaWidgets.insert(ADS_NS::BottomDropArea, createDropIndicatorWidget(BottomDropArea));//createDropWidget(":/img/split-bottom.png"));
areaWidgets.insert(ADS_NS::LeftDropArea, createDropIndicatorWidget(LeftDropArea));//createDropWidget(":/img/split-left.png"));
areaWidgets.insert(ADS_NS::CenterDropArea, createDropIndicatorWidget(CenterDropArea));//createDropWidget(":/img/dock-center.png"));
_cross->setAreaWidgets(areaWidgets);
_cross->setVisible(false);
setVisible(false);
}
DropOverlay::~DropOverlay()
{
if (_splitAreas)
{
delete _splitAreas;
_splitAreas = NULL;
}
}
void DropOverlay::setAllowedAreas(DropAreas areas)
{
if (areas == _allowedAreas)
return;
_allowedAreas = areas;
_cross->reset();
}
DropAreas DropOverlay::allowedAreas() const
{
return _allowedAreas;
}
void DropOverlay::setAreaWidgets(const QHash<DropArea, QWidget*>& widgets)
{
_cross->setAreaWidgets(widgets);
}
DropArea DropOverlay::cursorLocation() const
{
DropArea loc = InvalidDropArea;
if (_splitAreas)
return _cross->cursorLocation();
}
DropArea DropOverlay::showDropOverlay(QWidget* target)
{
if (_target == target)
{
loc = _splitAreas->cursorLocation();
// Hint: We could update geometry of overlay here.
DropArea da = cursorLocation();
if (da != _lastLocation)
{
repaint();
_lastLocation = da;
}
return da;
}
return loc;
hideDropOverlay();
_fullAreaDrop = false;
_target = target;
_targetRect = QRect();
_lastLocation = InvalidDropArea;
// Move it over the target.
resize(target->size());
move(target->mapToGlobal(target->rect().topLeft()));
show();
return cursorLocation();
}
void DropOverlay::showDropOverlay(QWidget* target, const QRect& targetAreaRect)
{
if (_target == target && _targetRect == targetAreaRect)
{
return;
}
hideDropOverlay();
_fullAreaDrop = true;
_target = target;
_targetRect = targetAreaRect;
_lastLocation = InvalidDropArea;
// Move it over the target's area.
resize(targetAreaRect.size());
move(target->mapToGlobal(QPoint(targetAreaRect.x(), targetAreaRect.y())));
show();
return;
}
void DropOverlay::hideDropOverlay()
{
hide();
_fullAreaDrop = false;
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
_target.clear();
#else
_target = 0;
#endif
_targetRect = QRect();
_lastLocation = InvalidDropArea;
}
void DropOverlay::paintEvent(QPaintEvent*)
{
QPainter p(this);
const QColor areaColor = palette().color(QPalette::Active, QPalette::Highlight);//QColor(0, 100, 255)
// Always draw drop-rect over the entire rect()
if (_fullAreaDrop)
{
QRect r = rect();
p.fillRect(r, QBrush(QColor(0, 100, 255), Qt::Dense4Pattern));
p.setBrush(QBrush(QColor(0, 100, 255)));
p.fillRect(r, QBrush(areaColor, Qt::Dense4Pattern));
p.setBrush(QBrush(areaColor));
p.drawRect(r);
return;
}
@@ -103,8 +277,8 @@ void DropOverlay::paintEvent(QPaintEvent*)
}
if (!r.isNull())
{
p.fillRect(r, QBrush(QColor(0, 100, 255), Qt::Dense4Pattern));
p.setBrush(QBrush(QColor(0, 100, 255)));
p.fillRect(r, QBrush(areaColor, Qt::Dense4Pattern));
p.setBrush(QBrush(areaColor));
p.drawRect(r);
}
@@ -115,71 +289,62 @@ void DropOverlay::paintEvent(QPaintEvent*)
// p.fillRect(r, QBrush(QColor(0, 100, 255), Qt::Dense4Pattern));
// p.setBrush(QBrush(QColor(0, 100, 255)));
// p.drawRect(r);
// p.drawRect(r);
}
void DropOverlay::showEvent(QShowEvent*)
{
_cross->show();
}
void DropOverlay::hideEvent(QHideEvent*)
{
_cross->hide();
}
void DropOverlay::resizeEvent(QResizeEvent* e)
{
// Keep it in center of DropOverlay
if (_splitAreas)
{
_splitAreas->resize(e->size());
}
_cross->resize(e->size());
}
void DropOverlay::moveEvent(QMoveEvent* e)
{
// Keep it in center of DropOverlay
if (_splitAreas)
{
_splitAreas->move(e->pos());
}
_cross->move(e->pos());
}
///////////////////////////////////////////////////////////////////////
DropSplitAreas::DropSplitAreas(DropAreas areas, QWidget* parent) :
AbstractDropAreas(parent),
_top(0),
_right(0),
_bottom(0),
_left(0),
_center(0)
static QPair<QPoint, int> gridPosForArea(const DropArea area)
{
switch (area)
{
case ADS_NS::TopDropArea:
return qMakePair(QPoint(0, 1), (int) Qt::AlignHCenter | Qt::AlignBottom);
case ADS_NS::RightDropArea:
return qMakePair(QPoint(1, 2), (int) Qt::AlignLeft | Qt::AlignVCenter);
case ADS_NS::BottomDropArea:
return qMakePair(QPoint(2, 1), (int) Qt::AlignHCenter | Qt::AlignTop);
case ADS_NS::LeftDropArea:
return qMakePair(QPoint(1, 0), (int) Qt::AlignRight | Qt::AlignVCenter);
case ADS_NS::CenterDropArea:
return qMakePair(QPoint(1, 1), (int) Qt::AlignCenter);
default:
return QPair<QPoint, int>();
}
}
DropOverlayCross::DropOverlayCross(DropOverlay* overlay) :
QWidget(overlay->parentWidget()),
_overlay(overlay),
_widgets()
{
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
setWindowTitle("DropSplitAreas");
setWindowTitle("DropOverlayCross");
setAttribute(Qt::WA_TranslucentBackground);
QGridLayout* grid = new QGridLayout();
grid->setContentsMargins(0, 0, 0, 0);
grid->setSpacing(6);
if (areas.testFlag(ADS_NS::TopDropArea))
{
_top = createDropWidget(":/img/split-top.png");
grid->addWidget(_top, 0, 1, Qt::AlignHCenter | Qt::AlignBottom);
}
if (areas.testFlag(ADS_NS::RightDropArea))
{
_right = createDropWidget(":/img/split-right.png");
grid->addWidget(_right, 1, 2, Qt::AlignLeft | Qt::AlignVCenter);
}
if (areas.testFlag(ADS_NS::BottomDropArea))
{
_bottom = createDropWidget(":/img/split-bottom.png");
grid->addWidget(_bottom, 2, 1, Qt::AlignHCenter | Qt::AlignTop);
}
if (areas.testFlag(ADS_NS::LeftDropArea))
{
_left = createDropWidget(":/img/split-left.png");
grid->addWidget(_left, 1, 0, Qt::AlignRight | Qt::AlignVCenter);
}
if (areas.testFlag(ADS_NS::CenterDropArea))
{
_center = createDropWidget(":/img/dock-center.png");
grid->addWidget(_center, 1, 1, Qt::AlignCenter);
}
_grid = new QGridLayout();
_grid->setContentsMargins(0, 0, 0, 0);
_grid->setSpacing(6);
QBoxLayout* bl1 = new QBoxLayout(QBoxLayout::TopToBottom);
bl1->setContentsMargins(0, 0, 0, 0);
@@ -193,107 +358,83 @@ DropSplitAreas::DropSplitAreas(DropAreas areas, QWidget* parent) :
bl1->addStretch(1);
bl1->addLayout(bl2);
bl2->addStretch(1);
bl2->addLayout(grid, 0);
bl2->addLayout(_grid, 0);
bl2->addStretch(1);
bl1->addStretch(1);
}
DropArea DropSplitAreas::cursorLocation() const
DropOverlayCross::~DropOverlayCross()
{
DropArea loc = InvalidDropArea;
QPoint pos = mapFromGlobal(QCursor::pos());
if (_top && _top->geometry().contains(pos))
{
loc = TopDropArea;
}
else if (_right && _right->geometry().contains(pos))
{
loc = RightDropArea;
}
else if (_bottom && _bottom->geometry().contains(pos))
{
loc = BottomDropArea;
}
else if (_left && _left->geometry().contains(pos))
{
loc = LeftDropArea;
}
else if (_center && _center->geometry().contains(pos))
{
loc = CenterDropArea;
}
return loc;
}
// Globals ////////////////////////////////////////////////////////////
static QPointer<DropOverlay> MyOverlay;
static QPointer<QWidget> MyOverlayParent;
static QRect MyOverlayParentRect;
static DropArea MyOverlayLastLocation = InvalidDropArea;
DropArea showDropOverlay(QWidget* parent, DropAreas areas)
void DropOverlayCross::setAreaWidgets(const QHash<DropArea, QWidget*>& widgets)
{
if (MyOverlay)
// Delete old widgets.
QMutableHashIterator<DropArea, QWidget*> i(_widgets);
while (i.hasNext())
{
if (MyOverlayParent == parent)
i.next();
QWidget* widget = i.value();
_grid->removeWidget(widget);
delete widget;
i.remove();
}
// Insert new widgets into grid.
_widgets = widgets;
QHashIterator<DropArea, QWidget*> i2(_widgets);
while (i2.hasNext())
{
i2.next();
const DropArea area = i2.key();
QWidget* widget = i2.value();
const QPair<QPoint, int> opts = gridPosForArea(area);
_grid->addWidget(widget, opts.first.x(), opts.first.y(), (Qt::Alignment) opts.second);
}
reset();
}
DropArea DropOverlayCross::cursorLocation() const
{
const QPoint pos = mapFromGlobal(QCursor::pos());
QHashIterator<DropArea, QWidget*> i(_widgets);
while (i.hasNext())
{
i.next();
if (_overlay->allowedAreas().testFlag(i.key())
&& i.value()
&& i.value()->isVisible()
&& i.value()->geometry().contains(pos))
{
// Hint: We could update geometry of overlay here.
DropArea da = MyOverlay->cursorLocation();
if (da != MyOverlayLastLocation)
{
MyOverlay->repaint();
MyOverlayLastLocation = da;
}
return da;
return i.key();
}
hideDropOverlay();
}
// Create new overlay and move it directly over the "parent" widget.
MyOverlay = new DropOverlay(areas, parent);
MyOverlay->resize(parent->size());
MyOverlay->move(parent->mapToGlobal(parent->rect().topLeft()));
MyOverlay->show();
MyOverlayParent = parent;
return MyOverlay->cursorLocation();
return InvalidDropArea;
}
void showDropOverlay(QWidget* parent, const QRect& areaRect, DropAreas areas)
void DropOverlayCross::showEvent(QShowEvent*)
{
if (MyOverlay)
resize(_overlay->size());
move(_overlay->pos());
}
void DropOverlayCross::reset()
{
QList<DropArea> allAreas;
allAreas << ADS_NS::TopDropArea << ADS_NS::RightDropArea << ADS_NS::BottomDropArea << ADS_NS::LeftDropArea << ADS_NS::CenterDropArea;
const DropAreas allowedAreas = _overlay->allowedAreas();
// Update visibility of area widgets based on allowedAreas.
for (int i = 0; i < allAreas.count(); ++i)
{
if (MyOverlayParent == parent && MyOverlayParentRect == areaRect)
const QPair<QPoint, int> opts = gridPosForArea(allAreas.at(i));
QLayoutItem* item = _grid->itemAtPosition(opts.first.x(), opts.first.y());
QWidget* w = NULL;
if (item && (w = item->widget()) != NULL)
{
return;
w->setVisible(allowedAreas.testFlag(allAreas.at(i)));
}
hideDropOverlay();
}
// Create overlay and move it to the parent's areaRect
MyOverlay = new DropOverlay(areas, parent);
MyOverlay->setFullAreaDropEnabled(true);
MyOverlay->resize(areaRect.size());
MyOverlay->move(parent->mapToGlobal(QPoint(areaRect.x(), areaRect.y())));
MyOverlay->show();
MyOverlayParent = parent;
MyOverlayParentRect = areaRect;
return;
}
void hideDropOverlay()
{
if (MyOverlay)
{
MyOverlay->hide();
delete MyOverlay;
#if QT_VERSION >= 0x050000
MyOverlayParent.clear();
#else
MyOverlayParent = 0;
#endif
MyOverlayParentRect = QRect();
MyOverlayLastLocation = InvalidDropArea;
}
}

View File

@@ -1,7 +1,5 @@
#include "ads/FloatingWidget.h"
#include <QDebug>
#include <QBoxLayout>
#include <QPushButton>
#include <QSizePolicy>
@@ -33,15 +31,21 @@ FloatingWidget::FloatingWidget(ContainerWidget* container, SectionContent::RefPt
l->addLayout(_titleLayout, 0);
titleWidget->setActiveTab(false);
QPushButton* closeButton = new QPushButton();
closeButton->setObjectName("closeButton");
closeButton->setFlat(true);
closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
closeButton->setToolTip(tr("Close"));
closeButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
_titleLayout->addWidget(closeButton);
//QObject::connect(closeButton, &QPushButton::clicked, this, &FloatingWidget::close);
QObject::connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
if (sc->flags().testFlag(SectionContent::Closeable))
{
QPushButton* closeButton = new QPushButton();
closeButton->setObjectName("closeButton");
closeButton->setFlat(true);
closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
closeButton->setToolTip(tr("Close"));
closeButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
_titleLayout->addWidget(closeButton);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(closeButton, &QPushButton::clicked, this, &FloatingWidget::onCloseButtonClicked);
#else
QObject::connect(closeButton, SIGNAL(clicked(bool)), this, SLOT(onCloseButtonClicked()));
#endif
}
// Content
l->addWidget(contentWidget, 1);
@@ -52,7 +56,6 @@ FloatingWidget::FloatingWidget(ContainerWidget* container, SectionContent::RefPt
FloatingWidget::~FloatingWidget()
{
qDebug() << Q_FUNC_INFO;
_container->_floatings.removeAll(this); // Note: I don't like this here, but we have to remove it from list...
}
@@ -73,4 +76,9 @@ bool FloatingWidget::takeContent(InternalContentData& data)
return true;
}
void FloatingWidget::onCloseButtonClicked()
{
_container->hideSectionContent(_content);
}
ADS_NAMESPACE_END

View File

@@ -1,6 +1,5 @@
#include "ads/SectionContent.h"
#include <QDebug>
#include <QWidget>
#include <QLabel>
@@ -10,7 +9,8 @@
ADS_NAMESPACE_BEGIN
SectionContent::SectionContent() :
_uid(GetNextUid())
_uid(GetNextUid()),
_flags(AllFlags)
{
}
@@ -79,6 +79,11 @@ QWidget* SectionContent::contentWidget() const
return _contentWidget;
}
SectionContent::Flags SectionContent::flags() const
{
return _flags;
}
QString SectionContent::visibleTitle() const
{
if (_title.isEmpty())
@@ -96,6 +101,11 @@ void SectionContent::setTitle(const QString& title)
_title = title;
}
void SectionContent::setFlags(const Flags f)
{
_flags = f;
}
int SectionContent::GetNextUid()
{
static int NextUid = 0;

View File

@@ -1,6 +1,5 @@
#include "ads/SectionContentWidget.h"
#include <QDebug>
#include <QBoxLayout>
ADS_NAMESPACE_BEGIN
@@ -9,8 +8,6 @@ SectionContentWidget::SectionContentWidget(SectionContent::RefPtr c, QWidget* pa
QFrame(parent),
_content(c)
{
qDebug() << Q_FUNC_INFO;
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom);
l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(0);
@@ -20,7 +17,7 @@ SectionContentWidget::SectionContentWidget(SectionContent::RefPtr c, QWidget* pa
SectionContentWidget::~SectionContentWidget()
{
qDebug() << Q_FUNC_INFO;
layout()->removeWidget(_content->contentWidget());
}
ADS_NAMESPACE_END

View File

@@ -1,6 +1,5 @@
#include "ads/SectionTitleWidget.h"
#include <QDebug>
#include <QString>
#include <QApplication>
#include <QBoxLayout>
@@ -31,8 +30,6 @@ SectionTitleWidget::SectionTitleWidget(SectionContent::RefPtr content, QWidget*
_tabMoving(false),
_activeTab(false)
{
qDebug() << Q_FUNC_INFO;
QBoxLayout* l = new QBoxLayout(QBoxLayout::LeftToRight);
l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(0);
@@ -42,7 +39,7 @@ SectionTitleWidget::SectionTitleWidget(SectionContent::RefPtr content, QWidget*
SectionTitleWidget::~SectionTitleWidget()
{
qDebug() << Q_FUNC_INFO;
layout()->removeWidget(_content->titleWidget());
}
bool SectionTitleWidget::isActiveTab() const
@@ -66,11 +63,10 @@ void SectionTitleWidget::setActiveTab(bool active)
void SectionTitleWidget::mousePressEvent(QMouseEvent* ev)
{
// qDebug() << Q_FUNC_INFO << ev->pos();
if (ev->button() == Qt::LeftButton)
{
_dragStartPos = ev->pos();
ev->accept();
_dragStartPos = ev->pos();
return;
}
QFrame::mousePressEvent(ev);
@@ -78,24 +74,24 @@ void SectionTitleWidget::mousePressEvent(QMouseEvent* ev)
void SectionTitleWidget::mouseReleaseEvent(QMouseEvent* ev)
{
// qDebug() << Q_FUNC_INFO << ev->pos();
SectionWidget* section = NULL;
ContainerWidget* cw = findParentContainerWidget(this);
// Drop contents of FloatingWidget into SectionWidget.
if (_fw)
{
ContainerWidget* cw = findParentContainerWidget(this);
SectionWidget* sw = cw->sectionAt(cw->mapFromGlobal(ev->globalPos()));
if (sw)
{
DropArea loc = showDropOverlay(sw);
cw->_dropOverlay->setAllowedAreas(ADS_NS::AllAreas);
DropArea loc = cw->_dropOverlay->showDropOverlay(sw);
if (loc != InvalidDropArea)
{
#if !defined(ADS_ANIMATIONS_ENABLED)
InternalContentData data;
_fw->takeContent(data);
_fw->deleteLater();
#if QT_VERSION >= 0x050000
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
_fw.clear();
#else
_fw = 0;
@@ -145,7 +141,7 @@ void SectionTitleWidget::mouseReleaseEvent(QMouseEvent* ev)
InternalContentData data;
_fw->takeContent(data);
_fw->deleteLater();
#if QT_VERSION >= 0x050000
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
_fw.clear();
#else
_fw = 0;
@@ -160,16 +156,12 @@ void SectionTitleWidget::mouseReleaseEvent(QMouseEvent* ev)
else if (_tabMoving
&& (section = findParentSectionWidget(this)) != NULL)
{
qDebug() << "Stop tab move";
// Find tab under mouse
QPoint pos = ev->globalPos();
pos = section->mapFromGlobal(pos);
const int fromIndex = section->indexOfContent(_content);
const int toIndex = section->indexOfContentByTitlePos(pos, this);
qDebug() << "from" << fromIndex << "to" << toIndex;
section->moveContent(fromIndex, toIndex);
section->layout()->update();
}
if (!_dragStartPos.isNull())
@@ -178,7 +170,7 @@ void SectionTitleWidget::mouseReleaseEvent(QMouseEvent* ev)
// Reset
_dragStartPos = QPoint();
_tabMoving = false;
hideDropOverlay();
cw->_dropOverlay->hideDropOverlay();
QFrame::mouseReleaseEvent(ev);
}
@@ -188,8 +180,10 @@ void SectionTitleWidget::mouseMoveEvent(QMouseEvent* ev)
SectionWidget* section = NULL;
// Move already existing FloatingWidget
if (_fw)
if (_fw && (ev->buttons() & Qt::LeftButton))
{
ev->accept();
const QPoint moveToPos = ev->globalPos() - (_dragStartPos + QPoint(ADS_WINDOW_FRAME_BORDER_WIDTH, ADS_WINDOW_FRAME_BORDER_WIDTH));
_fw->move(moveToPos);
@@ -200,33 +194,36 @@ void SectionTitleWidget::mouseMoveEvent(QMouseEvent* ev)
section = cw->sectionAt(cw->mapFromGlobal(QCursor::pos()));
if (section)
{
showDropOverlay(section);
cw->_dropOverlay->setAllowedAreas(ADS_NS::AllAreas);
cw->_dropOverlay->showDropOverlay(section);
}
// Mouse is at the edge of the ContainerWidget
// Top, Right, Bottom, Left
else if (cw->outerTopDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
{
showDropOverlay(cw, cw->outerTopDropRect(), ADS_NS::TopDropArea);
cw->_dropOverlay->setAllowedAreas(ADS_NS::TopDropArea);
cw->_dropOverlay->showDropOverlay(cw, cw->outerTopDropRect());
}
else if (cw->outerRightDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
{
showDropOverlay(cw, cw->outerRightDropRect(), ADS_NS::RightDropArea);
cw->_dropOverlay->setAllowedAreas(ADS_NS::RightDropArea);
cw->_dropOverlay->showDropOverlay(cw, cw->outerRightDropRect());
}
else if (cw->outerBottomDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
{
showDropOverlay(cw, cw->outerBottomDropRect(), ADS_NS::BottomDropArea);
cw->_dropOverlay->setAllowedAreas(ADS_NS::BottomDropArea);
cw->_dropOverlay->showDropOverlay(cw, cw->outerBottomDropRect());
}
else if (cw->outerLeftDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
{
showDropOverlay(cw, cw->outerLeftDropRect(), ADS_NS::LeftDropArea);
cw->_dropOverlay->setAllowedAreas(ADS_NS::LeftDropArea);
cw->_dropOverlay->showDropOverlay(cw, cw->outerLeftDropRect());
}
else
{
hideDropOverlay();
cw->_dropOverlay->hideDropOverlay();
}
}
ev->accept();
return;
}
// Begin to drag/float the SectionContent.
@@ -234,6 +231,8 @@ void SectionTitleWidget::mouseMoveEvent(QMouseEvent* ev)
&& (section = findParentSectionWidget(this)) != NULL
&& !section->titleAreaGeometry().contains(section->mapFromGlobal(ev->globalPos())))
{
ev->accept();
// Create floating widget.
InternalContentData data;
if (!section->takeContent(_content->uid(), data))
@@ -245,7 +244,6 @@ void SectionTitleWidget::mouseMoveEvent(QMouseEvent* ev)
_fw = new FloatingWidget(cw, data.content, data.titleWidget, data.contentWidget, cw);
_fw->resize(section->size());
cw->_floatings.append(_fw); // Note: I don't like this...
//setActiveTab(false);
const QPoint moveToPos = ev->globalPos() - (_dragStartPos + QPoint(ADS_WINDOW_FRAME_BORDER_WIDTH, ADS_WINDOW_FRAME_BORDER_WIDTH));
_fw->move(moveToPos);
@@ -257,23 +255,22 @@ void SectionTitleWidget::mouseMoveEvent(QMouseEvent* ev)
delete section;
section = NULL;
}
// Delete old splitter, if it is empty now
deleteEmptySplitter(cw);
ev->accept();
return;
}
// Handle movement of this tab
else if (_tabMoving
&& (section = findParentSectionWidget(this)) != NULL)
{
ev->accept();
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom);
QPoint moveToPos = mapToParent(ev->pos()) - _dragStartPos;
moveToPos.setY(0/* + top*/);
move(moveToPos);
ev->accept();
return;
}
// Begin to drag title inside the title area to switch its position inside the SectionWidget.
else if (!_dragStartPos.isNull() && (ev->buttons() & Qt::LeftButton)
@@ -281,10 +278,12 @@ void SectionTitleWidget::mouseMoveEvent(QMouseEvent* ev)
&& (section = findParentSectionWidget(this)) != NULL
&& section->titleAreaGeometry().contains(section->mapFromGlobal(ev->globalPos())))
{
// Raise current title-widget above other tabs
_tabMoving = true;
raise();
ev->accept();
_tabMoving = true;
raise(); // Raise current title-widget above other tabs
return;
}
QFrame::mouseMoveEvent(ev);
}

View File

@@ -4,12 +4,15 @@
#include <QBoxLayout>
#include <QStackedLayout>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QDragEnterEvent>
#include <QMimeData>
#include <QPainter>
#include <QStyle>
#include <QSplitter>
#include <QPushButton>
#include <QScrollBar>
#include <QMenu>
#if defined(ADS_ANIMATIONS_ENABLED)
#include <QGraphicsDropShadowEffect>
@@ -25,15 +28,12 @@
ADS_NAMESPACE_BEGIN
//int SectionWidget::NextUid = 1;
//QHash<int, SectionWidget*> SectionWidget::LookupMap;
//QHash<ContainerWidget*, QHash<int, SectionWidget*> > SectionWidget::LookupMapByContainer;
SectionWidget::SectionWidget(ContainerWidget* parent) :
QFrame(parent),
_uid(GetNextUid()),
_container(parent),
_tabsLayout(NULL),
_tabsLayoutInitCount(0),
_contentsLayout(NULL),
_mousePressTitleWidget(NULL)
{
@@ -42,25 +42,55 @@ SectionWidget::SectionWidget(ContainerWidget* parent) :
l->setSpacing(0);
setLayout(l);
/* top area with tabs and close button */
_topLayout = new QBoxLayout(QBoxLayout::LeftToRight);
_topLayout->setContentsMargins(0, 0, 0, 0);
_topLayout->setSpacing(0);
l->addLayout(_topLayout);
_tabsScrollArea = new SectionWidgetTabsScrollArea(this);
_topLayout->addWidget(_tabsScrollArea, 1);
_tabsContainerWidget = new QWidget();
_tabsContainerWidget->setObjectName("tabsContainerWidget");
_tabsScrollArea->setWidget(_tabsContainerWidget);
_tabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
_tabsLayout->setContentsMargins(0, 0, 0, 0);
_tabsLayout->setSpacing(0);
_tabsLayout->addStretch(1);
l->addLayout(_tabsLayout);
_tabsContainerWidget->setLayout(_tabsLayout);
QPushButton* closeButton = new QPushButton();
closeButton->setObjectName("closeButton");
closeButton->setFlat(true);
closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
closeButton->setToolTip(tr("Close"));
closeButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
_tabsLayout->addWidget(closeButton);
#if QT_VERSION >= 0x050000
QObject::connect(closeButton, &QPushButton::clicked, this, &SectionWidget::onCloseButtonClicked);
_tabsMenuButton = new QPushButton();
_tabsMenuButton->setObjectName("tabsMenuButton");
_tabsMenuButton->setFlat(true);
_tabsMenuButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarUnshadeButton));
_tabsMenuButton->setMaximumWidth(_tabsMenuButton->iconSize().width());
_topLayout->addWidget(_tabsMenuButton, 0);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
//QObject::connect(_tabsMenuButton, &QPushButton::clicked, this, &SectionWidget::onTabsMenuButtonClicked);
#else
QObject::connect(closeButton, SIGNAL(clicked(bool)), this, SLOT(onCloseButtonClicked()));
//QObject::connect(_tabsMenuButton, SIGNAL(clicked()), this, SLOT(onTabsMenuButtonClicked()));
#endif
_closeButton = new QPushButton();
_closeButton->setObjectName("closeButton");
_closeButton->setFlat(true);
_closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
_closeButton->setToolTip(tr("Close"));
_closeButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
_topLayout->addWidget(_closeButton, 0);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(_closeButton, &QPushButton::clicked, this, &SectionWidget::onCloseButtonClicked);
#else
QObject::connect(_closeButton, SIGNAL(clicked(bool)), this, SLOT(onCloseButtonClicked()));
#endif
_tabsLayoutInitCount = _tabsLayout->count();
/* central area with contents */
_contentsLayout = new QStackedLayout();
_contentsLayout->setContentsMargins(0, 0, 0, 0);
_contentsLayout->setSpacing(0);
@@ -78,7 +108,6 @@ SectionWidget::SectionWidget(ContainerWidget* parent) :
SectionWidget::~SectionWidget()
{
qDebug() << Q_FUNC_INFO;
if (_container)
{
SWLookupMapById(_container).remove(_uid);
@@ -90,7 +119,6 @@ SectionWidget::~SectionWidget()
if (splitter && splitter->count() == 0)
{
splitter->deleteLater();
qDebug() << "Delete empty splitter";
}
}
@@ -106,7 +134,7 @@ ContainerWidget* SectionWidget::containerWidget() const
QRect SectionWidget::titleAreaGeometry() const
{
return _tabsLayout->geometry();
return _topLayout->geometry();
}
QRect SectionWidget::contentAreaGeometry() const
@@ -120,8 +148,8 @@ void SectionWidget::addContent(const SectionContent::RefPtr& c)
SectionTitleWidget* title = new SectionTitleWidget(c, NULL);
_sectionTitles.append(title);
_tabsLayout->insertWidget(_tabsLayout->count() - 2, title);
#if QT_VERSION >= 0x050000
_tabsLayout->insertWidget(_tabsLayout->count() - _tabsLayoutInitCount, title);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(title, &SectionTitleWidget::clicked, this, &SectionWidget::onSectionTitleClicked);
#else
QObject::connect(title, SIGNAL(clicked()), this, SLOT(onSectionTitleClicked()));
@@ -137,6 +165,8 @@ void SectionWidget::addContent(const SectionContent::RefPtr& c)
// Switch to newest.
// else
// setCurrentIndex(_contentsLayout->count() - 1);
updateTabsMenu();
}
void SectionWidget::addContent(const InternalContentData& data, bool autoActivate)
@@ -146,9 +176,9 @@ void SectionWidget::addContent(const InternalContentData& data, bool autoActivat
// Add title-widget to tab-bar
// #FIX: Make it visible, since it is possible that it was hidden previously.
_sectionTitles.append(data.titleWidget);
_tabsLayout->insertWidget(_tabsLayout->count() - 2, data.titleWidget);
_tabsLayout->insertWidget(_tabsLayout->count() - _tabsLayoutInitCount, data.titleWidget);
data.titleWidget->setVisible(true);
#if QT_VERSION >= 0x050000
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(data.titleWidget, &SectionTitleWidget::clicked, this, &SectionWidget::onSectionTitleClicked);
#else
QObject::connect(data.titleWidget, SIGNAL(clicked()), this, SLOT(onSectionTitleClicked()));
@@ -168,6 +198,8 @@ void SectionWidget::addContent(const InternalContentData& data, bool autoActivat
// Mark it as inactive tab.
else
data.titleWidget->setActiveTab(false); // or: setCurrentIndex(currentIndex())
updateTabsMenu();
}
bool SectionWidget::takeContent(int uid, InternalContentData& data)
@@ -191,8 +223,14 @@ bool SectionWidget::takeContent(int uid, InternalContentData& data)
if (title)
{
_tabsLayout->removeWidget(title);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
title->setAttribute(Qt::WA_WState_Created, false); /* fix: floating rubberband #16 */
#endif
title->disconnect(this);
title->setParent(_container);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
title->setAttribute(Qt::WA_WState_Created, true); /* fix: floating rubberband #16 */
#endif
}
// Content wrapper widget (CONTENT)
@@ -213,6 +251,8 @@ bool SectionWidget::takeContent(int uid, InternalContentData& data)
setCurrentIndex(0);
}
updateTabsMenu();
data.content = sc;
data.titleWidget = title;
data.contentWidget = content;
@@ -224,6 +264,16 @@ int SectionWidget::indexOfContent(const SectionContent::RefPtr& c) const
return _contents.indexOf(c);
}
int SectionWidget::indexOfContentByUid(int uid) const
{
for (int i = 0; i < _contents.count(); ++i)
{
if (_contents[i]->uid() == uid)
return i;
}
return -1;
}
int SectionWidget::indexOfContentByTitlePos(const QPoint& p, QWidget* exclude) const
{
int index = -1;
@@ -247,19 +297,18 @@ void SectionWidget::moveContent(int from, int to)
{
if (from >= _contents.size() || from < 0 || to >= _contents.size() || to < 0 || from == to)
{
qCritical() << "Invalid index for tab movement" << from << to;
qDebug() << "Invalid index for tab movement" << from << to;
_tabsLayout->update();
return;
}
SectionContent::RefPtr sc = _contents.at(from);
_contents.move(from, to);
_sectionTitles.move(from, to);
_sectionContents.move(from, to);
QLayoutItem* liFrom = NULL;
liFrom = _tabsLayout->takeAt(from);
#if QT_VERSION >= 0x050000
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
_tabsLayout->insertItem(to, liFrom);
#else
_tabsLayout->insertWidget(to, liFrom->widget());
@@ -270,6 +319,13 @@ void SectionWidget::moveContent(int from, int to)
liFrom = _contentsLayout->takeAt(from);
_contentsLayout->insertWidget(to, liFrom->widget());
delete liFrom;
updateTabsMenu();
}
void SectionWidget::showEvent(QShowEvent*)
{
_tabsScrollArea->ensureWidgetVisible(_sectionTitles.at(currentIndex()));
}
void SectionWidget::setCurrentIndex(int index)
@@ -279,7 +335,6 @@ void SectionWidget::setCurrentIndex(int index)
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
return;
}
qDebug() << Q_FUNC_INFO << index << QString("section=%1; content=%2").arg(_uid).arg(_contents.at(index)->uniqueName());
// Set active TAB
for (int i = 0; i < _tabsLayout->count(); ++i)
@@ -291,7 +346,14 @@ void SectionWidget::setCurrentIndex(int index)
if (stw)
{
if (i == index)
{
stw->setActiveTab(true);
_tabsScrollArea->ensureWidgetVisible(stw);
if (stw->_content->flags().testFlag(SectionContent::Closeable))
_closeButton->setEnabled(true);
else
_closeButton->setEnabled(false);
}
else
stw->setActiveTab(false);
}
@@ -314,7 +376,6 @@ void SectionWidget::onSectionTitleClicked()
void SectionWidget::onCloseButtonClicked()
{
qDebug() << Q_FUNC_INFO << currentIndex();
const int index = currentIndex();
if (index < 0 || index > _contents.size() - 1)
return;
@@ -324,23 +385,74 @@ void SectionWidget::onCloseButtonClicked()
_container->hideSectionContent(sc);
}
void SectionWidget::onTabsMenuActionTriggered(bool)
{
QAction* a = qobject_cast<QAction*>(sender());
if (a)
{
const int uid = a->data().toInt();
const int index = indexOfContentByUid(uid);
if (index >= 0)
setCurrentIndex(index);
}
}
void SectionWidget::updateTabsMenu()
{
QMenu* m = new QMenu();
for (int i = 0; i < _contents.count(); ++i)
{
const SectionContent::RefPtr& sc = _contents.at(i);
QAction* a = m->addAction(QIcon(), sc->visibleTitle());
a->setData(sc->uid());
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(a, &QAction::triggered, this, &SectionWidget::onTabsMenuActionTriggered);
#else
QObject::connect(a, SIGNAL(triggered(bool)), this, SLOT(onTabsMenuActionTriggered(bool)));
#endif
}
QMenu* old = _tabsMenuButton->menu();
_tabsMenuButton->setMenu(m);
delete old;
}
int SectionWidget::GetNextUid()
{
static int NextUid = 0;
return ++NextUid;
}
//QHash<int, SectionWidget*>& SectionWidget::GetLookupMap()
//{
// static QHash<int, SectionWidget*> LookupMap;
// return LookupMap;
/*****************************************************************************/
//}
SectionWidgetTabsScrollArea::SectionWidgetTabsScrollArea(SectionWidget*,
QWidget* parent) :
QScrollArea(parent)
{
/* Important: QSizePolicy::Ignored makes the QScrollArea behaves
like a QLabel and automatically fits into the layout. */
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored);
setFrameStyle(QFrame::NoFrame);
setWidgetResizable(true);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
//QHash<ContainerWidget*, QHash<int, SectionWidget*> >& SectionWidget::GetLookupMapByContainer()
//{
// static QHash<ContainerWidget*, QHash<int, SectionWidget*> > LookupMapByContainer;
// return LookupMapByContainer;
//}
SectionWidgetTabsScrollArea::~SectionWidgetTabsScrollArea()
{
}
void SectionWidgetTabsScrollArea::wheelEvent(QWheelEvent* e)
{
e->accept();
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
const int direction = e->angleDelta().y();
#else
const int direction = e->delta();
#endif
if (direction < 0)
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20);
else
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20);
}
ADS_NAMESPACE_END

View File

@@ -0,0 +1,440 @@
#include "ads/Serialization.h"
#include <QDebug>
ADS_NAMESPACE_SER_BEGIN
/*
\namespace ads::serialization
Serialization of ContainerWidget
--------------------------------
# Data Format Header
quint32 Magic
quint32 Major Version
quint32 Minor Version
# Offsets of available contents
qint32 Number of offset headers
LOOP
qint32 Type (e.g. Hierachy, SectionIndex)
qint64 Offset
qint64 Length
# Type: Hierachy
# Used to recreate the GUI geometry and state.
int Number of floating widgets
LOOP Floating widgets
QString Unique name of content
QByteArray Geometry of floating widget
bool Visibility
int Number of layout items (Valid values: 0, 1)
IF 0
int Number of hidden contents
LOOP Contents
QString Unique name of content
ELSEIF 1
... todo ...
ENDIF
# Type: SectionIndex
# Can be used for quick lookups on details for SectionWidgets.
# It includes sizes and its contents.
qint32 Number of section-widgets
LOOP
qint32 Width
qint32 Height
qint32 Current active tab index
qint32 Number of contents
LOOP
QString Unique name of content
bool Visibility
qint32 Preferred tab index
*/
///////////////////////////////////////////////////////////////////////////////
qint32 HeaderEntity::MAGIC = 0x00001337;
qint32 HeaderEntity::MAJOR_VERSION = 2;
qint32 HeaderEntity::MINOR_VERSION = 0;
HeaderEntity::HeaderEntity() :
magic(0), majorVersion(0), minorVersion(0)
{
}
QDataStream& operator<<(QDataStream& out, const HeaderEntity& data)
{
out << data.magic;
out << data.majorVersion;
out << data.minorVersion;
return out;
}
QDataStream& operator>>(QDataStream& in, HeaderEntity& data)
{
in >> data.magic;
in >> data.majorVersion;
in >> data.minorVersion;
return in;
}
///////////////////////////////////////////////////////////////////////////////
OffsetsHeaderEntity::OffsetsHeaderEntity() :
entriesCount(0)
{
}
QDataStream& operator<<(QDataStream& out, const OffsetsHeaderEntity& data)
{
out << data.entriesCount;
for (int i = 0; i < data.entriesCount; ++i)
{
out << data.entries.at(i);
}
return out;
}
QDataStream& operator>>(QDataStream& in, OffsetsHeaderEntity& data)
{
in >> data.entriesCount;
for (int i = 0; i < data.entriesCount; ++i)
{
OffsetsHeaderEntryEntity entry;
in >> entry;
data.entries.append(entry);
}
return in;
}
///////////////////////////////////////////////////////////////////////////////
OffsetsHeaderEntryEntity::OffsetsHeaderEntryEntity() :
type(ET_Unknown), offset(0), contentSize(0)
{
}
QDataStream& operator<<(QDataStream& out, const OffsetsHeaderEntryEntity& data)
{
out << data.type;
out << data.offset;
out << data.contentSize;
return out;
}
QDataStream& operator>>(QDataStream& in, OffsetsHeaderEntryEntity& data)
{
in >> data.type;
in >> data.offset;
in >> data.contentSize;
return in;
}
///////////////////////////////////////////////////////////////////////////////
SectionEntity::SectionEntity() :
x(0), y(0), width(0), height(0), currentIndex(0), sectionContentsCount(0)
{
}
QDataStream& operator<<(QDataStream& out, const SectionEntity& data)
{
out << data.x;
out << data.y;
out << data.width;
out << data.height;
out << data.currentIndex;
out << data.sectionContentsCount;
for (int i = 0; i < data.sectionContentsCount; ++i)
{
out << data.sectionContents.at(i);
}
return out;
}
QDataStream& operator>>(QDataStream& in, SectionEntity& data)
{
in >> data.x;
in >> data.y;
in >> data.width;
in >> data.height;
in >> data.currentIndex;
in >> data.sectionContentsCount;
for (int i = 0; i < data.sectionContentsCount; ++i)
{
SectionContentEntity sc;
in >> sc;
data.sectionContents.append(sc);
}
return in;
}
///////////////////////////////////////////////////////////////////////////////
SectionContentEntity::SectionContentEntity() :
visible(false), preferredIndex(0)
{
}
QDataStream& operator<<(QDataStream& out, const SectionContentEntity& data)
{
out << data.uniqueName;
out << data.visible;
out << data.preferredIndex;
return out;
}
QDataStream& operator>>(QDataStream& in, SectionContentEntity& data)
{
in >> data.uniqueName;
in >> data.visible;
in >> data.preferredIndex;
return in;
}
///////////////////////////////////////////////////////////////////////////////
FloatingContentEntity::FloatingContentEntity() :
xpos(0), ypos(0), width(0), height(0), visible(false)
{
}
QDataStream& operator<<(QDataStream& out, const FloatingContentEntity& data)
{
out << data.uniqueName;
out << data.xpos;
out << data.ypos;
out << data.width;
out << data.height;
out << data.visible;
return out;
}
QDataStream& operator>>(QDataStream& in, FloatingContentEntity& data)
{
in >> data.uniqueName;
in >> data.xpos;
in >> data.ypos;
in >> data.width;
in >> data.height;
in >> data.visible;
return in;
}
///////////////////////////////////////////////////////////////////////////////
HierarchyData::HierarchyData()
{
}
QDataStream& operator<<(QDataStream& out, const HierarchyData& data)
{
out << data.data;
return out;
}
QDataStream& operator>>(QDataStream& in, HierarchyData& data)
{
in >> data.data;
return in;
}
///////////////////////////////////////////////////////////////////////////////
SectionIndexData::SectionIndexData() :
sectionsCount(0)
{
}
QDataStream& operator<<(QDataStream& out, const SectionIndexData& data)
{
out << data.sectionsCount;
for (int i = 0; i < data.sectionsCount; ++i)
{
out << data.sections.at(i);
}
return out;
}
QDataStream& operator>>(QDataStream& in, SectionIndexData& data)
{
in >> data.sectionsCount;
for (int i = 0; i < data.sectionsCount; ++i)
{
SectionEntity s;
in >> s;
data.sections.append(s);
}
return in;
}
///////////////////////////////////////////////////////////////////////////////
InMemoryWriter::InMemoryWriter()
{
_contentBuffer.open(QIODevice::ReadWrite);
}
bool InMemoryWriter::write(qint32 entryType, const QByteArray& data)
{
OffsetsHeaderEntryEntity entry;
entry.type = entryType;
entry.offset = _contentBuffer.pos(); // Relative offset!
entry.contentSize = data.size();
_contentBuffer.write(data);
_offsetsHeader.entries.append(entry);
_offsetsHeader.entriesCount += 1;
return true;
}
bool InMemoryWriter::write(const SectionIndexData& data)
{
OffsetsHeaderEntryEntity entry;
entry.type = ET_SectionIndex;
entry.offset = _contentBuffer.pos(); // Relative offset!
QDataStream out(&_contentBuffer);
out.setVersion(QDataStream::Qt_4_5);
out << data;
entry.contentSize = _contentBuffer.size() - entry.offset;
_offsetsHeader.entries.append(entry);
_offsetsHeader.entriesCount += 1;
return true;
}
QByteArray InMemoryWriter::toByteArray() const
{
QByteArray data;
QDataStream out(&data, QIODevice::ReadWrite);
out.setVersion(QDataStream::Qt_4_5);
// Basic format header.
HeaderEntity header;
header.magic = HeaderEntity::MAGIC;
header.majorVersion = HeaderEntity::MAJOR_VERSION;
header.minorVersion = HeaderEntity::MINOR_VERSION;
out << header;
// Offsets-Header
// - Save begin pos
// - Write OffsetsHeader
// - Convert relative- to absolute-offsets
// - Seek back to begin-pos and write OffsetsHeader again.
// Use a copy of OffsetsHeader to keep the _offsetsHeader relative.
const qint64 posOffsetHeaders = out.device()->pos();
OffsetsHeaderEntity offsetsHeader = _offsetsHeader;
out << offsetsHeader;
// Now we know the size of the entire header.
// We can update the relative- to absolute-offsets now.
const qint64 allHeaderSize = out.device()->pos();
for (int i = 0; i < offsetsHeader.entriesCount; ++i)
{
offsetsHeader.entries[i].offset += allHeaderSize; // Absolute offset!
}
// Seek back and write again with absolute offsets.
// TODO Thats not nice, but it works...
out.device()->seek(posOffsetHeaders);
out << offsetsHeader;
// Write contents.
out.writeRawData(_contentBuffer.data().constData(), _contentBuffer.size());
return data;
}
///////////////////////////////////////////////////////////////////////////////
InMemoryReader::InMemoryReader(const QByteArray& data) :
_data(data)
{
}
bool InMemoryReader::initReadHeader()
{
QDataStream in(_data);
in.setVersion(QDataStream::Qt_4_5);
// Basic format header.
HeaderEntity header;
in >> header;
if (header.magic != HeaderEntity::MAGIC)
{
qWarning() << QString("invalid format (magic=%1)").arg(header.magic);
return false;
}
if (header.majorVersion != HeaderEntity::MAJOR_VERSION)
{
qWarning() << QString("format is too new (major=%1; minor=%2)")
.arg(header.majorVersion).arg(header.minorVersion);
return false;
}
// OffsetsHeader.
in >> _offsetsHeader;
return !in.atEnd();
}
bool InMemoryReader::read(qint32 entryType, QByteArray& data)
{
// Find offset for "type".
int index = -1;
for (int i = 0; i < _offsetsHeader.entriesCount; ++i)
{
if (_offsetsHeader.entries.at(i).type == entryType)
{
index = i;
break;
}
}
if (index < 0)
return false;
else if (_offsetsHeader.entries.at(index).offset == 0)
return false;
const OffsetsHeaderEntryEntity& entry = _offsetsHeader.entries.at(index);
QDataStream in(_data);
in.setVersion(QDataStream::Qt_4_5);
in.device()->seek(entry.offset);
char* buff = new char[entry.contentSize];
in.readRawData(buff, entry.contentSize);
data.append(buff, entry.contentSize);
delete[] buff;
return true;
}
bool InMemoryReader::read(SectionIndexData& sid)
{
QByteArray sidData;
if (!read(ET_SectionIndex, sidData) || sidData.isEmpty())
return false;
QDataStream in(sidData);
in.setVersion(QDataStream::Qt_4_5);
in >> sid;
return in.atEnd();
}
///////////////////////////////////////////////////////////////////////////////
ADS_NAMESPACE_SER_END

View File

@@ -2,11 +2,6 @@ TARGET = AdvancedDockingSystemDemo
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TEMPLATE = app
INCLUDEPATH += $$PWD/src
greaterThan(QT_MAJOR_VERSION, 4): DEFINES += ADS_NAMESPACE_ENABLED
windows {
@@ -22,14 +17,19 @@ windows {
SOURCES += \
src/main.cpp \
src/mainwindow.cpp \
src/icontitlewidget.cpp
src/icontitlewidget.cpp \
src/dialogs/SectionContentListModel.cpp \
src/dialogs/SectionContentListWidget.cpp
HEADERS += \
src/mainwindow.h \
src/icontitlewidget.h
src/icontitlewidget.h \
src/dialogs/SectionContentListModel.h \
src/dialogs/SectionContentListWidget.h
FORMS += \
src/mainwindow.ui
src/mainwindow.ui \
src/dialogs/SectionContentListWidget.ui
# Dependency: AdvancedDockingSystem (staticlib)
@@ -47,9 +47,9 @@ FORMS += \
#else:unix: PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/libAdvancedDockingSystem.a
# Dependency: AdvancedDockingSystem (shared)
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/release/ -lAdvancedDockingSystem
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/debug/ -lAdvancedDockingSystem
else:unix: LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/ -lAdvancedDockingSystem
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/release/ -lAdvancedDockingSystem1
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/debug/ -lAdvancedDockingSystem1
else:unix: LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/ -lAdvancedDockingSystem1
INCLUDEPATH += $$PWD/../AdvancedDockingSystem/include
DEPENDPATH += $$PWD/../AdvancedDockingSystem/include

View File

@@ -0,0 +1,96 @@
#include "SectionContentListModel.h"
SectionContentListModel::SectionContentListModel(QObject* parent) :
QAbstractTableModel(parent)
{
_headers.insert(UidColumn, "UID");
_headers.insert(UniqueNameColumn, "Unique Name");
_headers.insert(TitleColumn, "Title");
_headers.insert(VisibleColumn, "Visible");
}
SectionContentListModel::~SectionContentListModel()
{
}
void SectionContentListModel::init(ADS_NS::ContainerWidget* cw)
{
#if QT_VERSION >= 0x050000
beginResetModel();
_cw = cw;
_contents = _cw->contents();
endResetModel();
#else
_cw = cw;
_contents = _cw->contents();
reset();
#endif
}
int SectionContentListModel::columnCount(const QModelIndex& parent) const
{
Q_UNUSED(parent)
return _headers.count();
}
QVariant SectionContentListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return _headers.value(section);
return QVariant();
}
int SectionContentListModel::rowCount(const QModelIndex& parent) const
{
Q_UNUSED(parent)
return _contents.count();
}
QVariant SectionContentListModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid() || index.row() > rowCount(index) - 1)
return QVariant();
const ADS_NS::SectionContent::RefPtr sc = _contents.at(index.row());
if (sc.isNull())
return QVariant();
switch (role)
{
case Qt::DisplayRole:
{
switch (index.column())
{
case UidColumn:
return sc->uid();
case UniqueNameColumn:
return sc->uniqueName();
case TitleColumn:
return sc->title();
case VisibleColumn:
return _cw->isSectionContentVisible(sc);
}
}
}
return QVariant();
}
bool SectionContentListModel::removeRows(int row, int count, const QModelIndex& parent)
{
if (row > rowCount(parent) - 1)
return false;
const int first = row;
const int last = row + count - 1;
beginRemoveRows(parent, first, last);
for (int i = last; i >= first; --i)
{
const ADS_NS::SectionContent::RefPtr sc = _contents.at(i);
_cw->removeSectionContent(sc);
_contents.removeAt(i);
}
endRemoveRows();
return true;
}

View File

@@ -0,0 +1,48 @@
#ifndef ADS_SECTIONCONTENTMODEL_H
#define ADS_SECTIONCONTENTMODEL_H
#include <QHash>
#include <QList>
#include <QString>
#include <QAbstractTableModel>
#include "ads/API.h"
#include "ads/ContainerWidget.h"
#include "ads/SectionContent.h"
ADS_NAMESPACE_BEGIN
class ContainerWidget;
ADS_NAMESPACE_END
class SectionContentListModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum Column
{
UidColumn,
UniqueNameColumn,
TitleColumn,
VisibleColumn
};
SectionContentListModel(QObject* parent);
virtual ~SectionContentListModel();
void init(ADS_NS::ContainerWidget* cw);
virtual int columnCount(const QModelIndex &parent) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
virtual int rowCount(const QModelIndex &parent) const;
virtual QVariant data(const QModelIndex &index, int role) const;
virtual bool removeRows(int row, int count, const QModelIndex &parent);
private:
QHash<int, QString> _headers;
ADS_NS::ContainerWidget* _cw;
QList<ADS_NS::SectionContent::RefPtr> _contents;
};
#endif

View File

@@ -0,0 +1,38 @@
#include "SectionContentListWidget.h"
#include "SectionContentListModel.h"
SectionContentListWidget::SectionContentListWidget(QWidget* parent) :
QDialog(parent)
{
_ui.setupUi(this);
connect(_ui.deleteButton, SIGNAL(clicked(bool)), this, SLOT(onDeleteButtonClicked()));
}
void SectionContentListWidget::setValues(const SectionContentListWidget::Values& v)
{
_v = v;
// Reset
QAbstractItemModel* m = _ui.tableView->model();
if (m)
{
_ui.tableView->setModel(NULL);
delete m;
m = NULL;
}
// Fill.
SectionContentListModel* sclm = new SectionContentListModel(this);
sclm->init(_v.cw);
_ui.tableView->setModel(sclm);
}
void SectionContentListWidget::onDeleteButtonClicked()
{
const QModelIndex mi = _ui.tableView->currentIndex();
if (!mi.isValid())
return;
_ui.tableView->model()->removeRows(mi.row(), 1, mi.parent());
}

View File

@@ -0,0 +1,33 @@
#ifndef SECTIONCONTENTLISTWIDGET
#define SECTIONCONTENTLISTWIDGET
#include <QDialog>
#include "ui_SectionContentListWidget.h"
#include "ads/API.h"
#include "ads/ContainerWidget.h"
#include "ads/SectionContent.h"
class SectionContentListWidget : public QDialog
{
Q_OBJECT
public:
class Values
{
public:
ADS_NS::ContainerWidget* cw;
};
SectionContentListWidget(QWidget* parent);
void setValues(const Values& v);
private slots:
void onDeleteButtonClicked();
private:
Ui::SectionContentListWidgetForm _ui;
Values _v;
};
#endif

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SectionContentListWidgetForm</class>
<widget class="QWidget" name="SectionContentListWidgetForm">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>522</width>
<height>258</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTableView" name="tableView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="deleteButton">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -6,12 +6,11 @@
static void initStyleSheet(QApplication& a)
{
QFile f(":/stylesheets/default-windows.css");
// QFile f(":/stylesheets/modern-windows.css");
// QFile f(":/stylesheets/vendor-partsolutions.css");
//Q_INIT_RESOURCE(ads); // If static linked.
QFile f(":ads/stylesheets/default-windows.css");
if (f.open(QFile::ReadOnly))
{
QByteArray ba = f.readAll();
const QByteArray ba = f.readAll();
f.close();
a.setStyleSheet(QString(ba));
}
@@ -20,7 +19,6 @@ static void initStyleSheet(QApplication& a)
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//Q_INIT_RESOURCE(ads);
a.setQuitOnLastWindowClosed(true);
initStyleSheet(a);

View File

@@ -11,6 +11,9 @@
#include <QBoxLayout>
#include "ads/SectionWidget.h"
#include "ads/DropOverlay.h"
#include "dialogs/SectionContentListWidget.h"
#include "icontitlewidget.h"
@@ -86,20 +89,36 @@ MainWindow::MainWindow(QWidget *parent) :
{
ui->setupUi(this);
// Setup actions.
QObject::connect(ui->actionContentList, SIGNAL(triggered()), this, SLOT(showSectionContentListDialog()));
// ADS - Create main container (ContainerWidget).
_container = new ADS_NS::ContainerWidget();
_container->setOrientation(Qt::Vertical);
#if QT_VERSION >= 0x050000
QObject::connect(_container, &ADS_NS::ContainerWidget::activeTabChanged, this, &MainWindow::onActiveTabChanged);
QObject::connect(_container, &ADS_NS::ContainerWidget::sectionContentVisibilityChanged, this, &MainWindow::onSectionContentVisibilityChanged);
#else
QObject::connect(_container, SIGNAL(activeTabChanged(const SectionContent::RefPtr&, bool)), this, SLOT(onActiveTabChanged(const SectionContent::RefPtr&, bool)));
QObject::connect(_container, SIGNAL(sectionContentVisibilityChanged(SectionContent::RefPtr,bool)), this, SLOT(onSectionContentVisibilityChanged(SectionContent::RefPtr,bool)));
#endif
setCentralWidget(_container);
// Optional: Use custom drop area widgets.
if (false)
{
QHash<ADS_NS::DropArea, QWidget*> areaWidgets;
areaWidgets.insert(ADS_NS::TopDropArea, new QPushButton("TOP"));
areaWidgets.insert(ADS_NS::RightDropArea, new QPushButton("RIGHT"));
areaWidgets.insert(ADS_NS::BottomDropArea, new QPushButton("BOTTOM"));
areaWidgets.insert(ADS_NS::LeftDropArea, new QPushButton("LEFT"));
areaWidgets.insert(ADS_NS::CenterDropArea, new QPushButton("CENTER"));
_container->dropOverlay()->setAreaWidgets(areaWidgets);
}
// ADS - Adding some contents.
// Test #1: Use high-level public API
if (true)
{
// Test #1: Use high-level public API
ADS_NS::ContainerWidget* cw = _container;
ADS_NS::SectionWidget* sw = NULL;
@@ -111,6 +130,24 @@ MainWindow::MainWindow(QWidget *parent) :
_container->addSectionContent(createLongTextLabelSC(_container));
_container->addSectionContent(createLongTextLabelSC(_container));
_container->addSectionContent(createLongTextLabelSC(_container));
ADS_NS::SectionContent::RefPtr sc = createLongTextLabelSC(cw);
sc->setFlags(ADS_NS::SectionContent::AllFlags ^ ADS_NS::SectionContent::Closeable);
_container->addSectionContent(sc);
}
else if (false)
{
// Issue #2: If the first drop is not into CenterDropArea, the application crashes.
ADS_NS::ContainerWidget* cw = _container;
ADS_NS::SectionWidget* sw = NULL;
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::LeftDropArea);
sw = _container->addSectionContent(createCalendarSC(cw), sw, ADS_NS::LeftDropArea);
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::CenterDropArea);
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::CenterDropArea);
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::CenterDropArea);
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::RightDropArea);
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::BottomDropArea);
}
// Default window geometry
@@ -126,6 +163,16 @@ MainWindow::~MainWindow()
delete ui;
}
void MainWindow::showSectionContentListDialog()
{
SectionContentListWidget::Values v;
v.cw = _container;
SectionContentListWidget w(this);
w.setValues(v);
w.exec();
}
void MainWindow::onActiveTabChanged(const ADS_NS::SectionContent::RefPtr& sc, bool active)
{
Q_UNUSED(active);
@@ -136,6 +183,11 @@ void MainWindow::onActiveTabChanged(const ADS_NS::SectionContent::RefPtr& sc, bo
}
}
void MainWindow::onSectionContentVisibilityChanged(const ADS_NS::SectionContent::RefPtr& sc, bool visible)
{
qDebug() << Q_FUNC_INFO << sc->uniqueName() << visible;
}
void MainWindow::onActionAddSectionContentTriggered()
{
return;

View File

@@ -18,11 +18,16 @@ public:
explicit MainWindow(QWidget *parent = 0);
virtual ~MainWindow();
public slots:
void showSectionContentListDialog();
private slots:
#if QT_VERSION >= 0x050000
void onActiveTabChanged(const ADS_NS::SectionContent::RefPtr& sc, bool active);
void onSectionContentVisibilityChanged(const ADS_NS::SectionContent::RefPtr& sc, bool visible);
#else
void onActiveTabChanged(const SectionContent::RefPtr& sc, bool active);
void onSectionContentVisibilityChanged(const SectionContent::RefPtr& sc, bool visible);
#endif
void onActionAddSectionContentTriggered();

View File

@@ -34,7 +34,7 @@
<property name="title">
<string>View</string>
</property>
<addaction name="actionDemo_1"/>
<addaction name="actionContentList"/>
<addaction name="actionDemo_2"/>
<addaction name="actionDemo_3"/>
</widget>
@@ -52,9 +52,9 @@
<string>Add SectionContent</string>
</property>
</action>
<action name="actionDemo_1">
<action name="actionContentList">
<property name="text">
<string>Demo 1</string>
<string>Contents...</string>
</property>
</action>
<action name="actionDemo_2">

View File

@@ -0,0 +1,7 @@
HEADERS += \
$$PWD/src/TestCore.h
SOURCES += \
$$PWD/src/main.cpp \
$$PWD/src/TestCore.cpp

View File

@@ -0,0 +1,14 @@
TARGET = AdvancedDockingSystemUnitTests
QT += core gui testlib
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
greaterThan(QT_MAJOR_VERSION, 4): DEFINES += ADS_NAMESPACE_ENABLED
DEFINES += ADS_IMPORT
INCLUDEPATH += $$PWD/src
INCLUDEPATH += $$PWD/../AdvancedDockingSystem/include
DEPENDPATH += $$PWD/../AdvancedDockingSystem/include
include(AdvancedDockingSystemUnitTests.pri)
include(../AdvancedDockingSystem/AdvancedDockingSystem.pri)

View File

@@ -0,0 +1,68 @@
#include "TestCore.h"
#include "ads/API.h"
#include "ads/Serialization.h"
void TestCore::serialization()
{
QList<QByteArray> datas;
datas.append(QByteArray("Custom Data Here!!!"));
datas.append(QByteArray("Even More..."));
datas.append(QByteArray("lalalaalalalalalalal").toBase64());
// WRITE some data.
ADS_NS_SER::InMemoryWriter writer;
for (int i = 0; i < datas.count(); ++i)
{
QVERIFY(writer.write(ADS_NS_SER::ET_Custom + i, datas.at(i)));
}
// Type: SectionIndexData
ADS_NS_SER::SectionIndexData sid;
for (int i = 0; i < 1; ++i)
{
ADS_NS_SER::SectionEntity se;
se.x = i;
se.y = i;
se.width = 100 + i;
se.height = 100 + i;
se.currentIndex = i;
for (int j = 0; j < 1; ++j)
{
ADS_NS_SER::SectionContentEntity sce;
sce.uniqueName = QString("uname-%1-%2").arg(i).arg(j);
sce.preferredIndex = 8;
sce.visible = true;
se.sectionContents.append(sce);
se.sectionContentsCount += 1;
}
sid.sections.append(se);
sid.sectionsCount += 1;
}
QVERIFY(writer.write(sid));
QVERIFY(writer.offsetsCount() == datas.count() + 1);
const QByteArray writtenData = writer.toByteArray();
QVERIFY(writtenData.size() > 0);
// READ and validate written data.
ADS_NS_SER::InMemoryReader reader(writtenData);
QVERIFY(reader.initReadHeader());
QVERIFY(reader.offsetsCount() == datas.count() + 1);
for (int i = 0; i < datas.count(); ++i)
{
QByteArray readData;
QVERIFY(reader.read(ADS_NS_SER::ET_Custom + i, readData));
QVERIFY(readData == datas.at(i));
}
// Type: SectionIndexData
ADS_NS_SER::SectionIndexData sidRead;
QVERIFY(reader.read(sidRead));
// TODO compare sidRead with sid
}
QTEST_MAIN(TestCore)

View File

@@ -0,0 +1,14 @@
#ifndef TEST_CORE_H
#define TEST_CORE_H
#include <QtTest/QtTest>
class TestCore : public QObject
{
Q_OBJECT
private slots:
void serialization();
};
#endif

View File

@@ -0,0 +1 @@
#include <QtTest/QtTest>

116
README.md
View File

@@ -1,4 +1,6 @@
# Advanced Docking System
# Advanced Docking System for Qt
[![Gitter](https://badges.gitter.im/mfreiholz/Qt-Advanced-Docking-System.svg)](https://gitter.im/mfreiholz/Qt-Advanced-Docking-System?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Manages content widgets more like Visual Studio or similar programs.
I also try to get everything done with basic Qt functionality.
Basic usage of QWidgets an QLayouts and using basic styles as much as possible.
@@ -7,9 +9,7 @@ Basic usage of QWidgets an QLayouts and using basic styles as much as possible.
![Dropping widgets](preview-dragndrop.png)
## Tested Compatible Environments
- Windows 10 / Qt 5.5.1 / VC12
- Windows 10 / Qt 5.5.1 / MinGW
- Windows 10 / Qt 4.5.3 / VC9
- Windows 7 / 8 / 8.1 / 10
- Ubuntu 15.10
## Build
@@ -20,6 +20,68 @@ You can run the demo project and test it yourself.
The `master` branch is not guaranteed to be stable or does not even build, since it is the main working branch.
If you want a version that builds, you should always use a release/beta tag.
## Getting started / Example
The following example shows the minimum code required to use ADS.
_MyWindow.h_
```cpp
#include <QMainWindow>
#include "ads/API.h"
#include "ads/ContainerWidget.h"
#include "ads/SectionContent.h"
class MyWindow : public QMainWindow
{
Q_OBJECT
public:
MyWindow(QWidget* parent);
private:
// The main container for dockings.
ADS_NS::ContainerWidget* _container;
// You always want to keep a reference of your content,
// in case you need to perform any action on it (show, hide, ...)
ADS_NS::SectionContent::RefPtr _sc1;
};
```
_MyWindow.cpp_
```cpp
#include "MyWindow.h"
#include <QLabel>
MyWindow::MyWindow(QWidget* parent) : QMainWindow(parent)
{
_container = new ADS_NS::ContainerWidget();
setCentralWidget(_container);
_sc1 = ADS_NS::SectionContent::newSectionContent(QString("Unique-Internal-Name"), _container, new QLabel("Visible Title"), new QLabel("Content Widget"));
_container->addSectionContent(_sc1, NULL, ADS_NS::CenterDropArea);
}
static void initStyleSheet(QApplication& a)
{
//Q_INIT_RESOURCE(ads); // If static linked.
QFile f(":ads/stylesheets/default-windows.css");
if (f.open(QFile::ReadOnly))
{
const QByteArray ba = f.readAll();
f.close();
a.setStyleSheet(QString(ba));
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(true);
initStyleSheet(a);
MainWindow mw;
mw.show();
return a.exec();
}
```
## Developers
[Manuel Freiholz](https://mfreiholz.de), Project Maintainer
@@ -30,49 +92,3 @@ This projects uses the [WTFPL license](http://www.wtfpl.net/)
(Do **W**hat **T**he **F**uck You Want To **P**ublic **L**icense)
Using it? Let us know by creating a [new issue](https://github.com/mfreiholz/qt-docks/issues/new) (You don't have to, of course).
## Credits
- Drop indicator images from [Code Project Article](http://www.codeproject.com/Articles/140209/Building-a-Docking-Window-Management-Solution-in-W)
## ToDo List & Changelog
Items sorted by priority
### Beta 0.2
- [ ] Use scrolling for SectionWidget tabs?
- [ ] It would be easier when the SectionTitleWidget and SectionContentWidget are created inside the "SectionContent::newSectionContent(..)" method.
This would make sure, that those two objects always exists.
- [ ] `ContainerWidget::showSectionContent` needs to insert the SC at the correct preferred position of SW
- [ ] It should be possible to drop a floating widget directly into the SW's tab-bar.
- [ ] Empty splitters, if only 2 or 1 items are in container
- [ ] Restore: Handle out-of-screen geometry for floating widgets
- [ ] Better handling of sizes when dropping contents. Currently it's unpredictable.
It might be good to use the same width/height as the parent content, if dropped on existing content.
In case of outer-drop we might use the preferred size of the content.
- [ ] Floating widget should be a real window with all of its functionality (maximize and split by moving it to the edge of the screen)
### Beta 0.1
- [x] Improve FloatingWidget (Remove maximize button, only support close-button which hides the widget)
- [x] Serialize and Deserialize state/size/positions of dockings
- [x] Make compatible with Qt 4.5 (\*ROFL!\*)
- [x] Save and restore FloatingWidget states
- [x] Restore: Manage new or deleted SectionContent objects, which are not available
- [x] Working with outer-edge-drops sometimes leaves empty splitters #BUG
- [x] Clean up of unused e.g. count()<=1 QSplitters doesn't work well #BUG
- [x] Show close button on right corner of SectionWidget. How to safe last section position?
- [x] Serialize state of `_hiddenSectionContents`
- [x] Add "title" to SectionContent object, which will be used in visible areas to display contents name.
- [x] It should be possible to catch the "activeTabChanged" signal for EXTERN_API users
- [x] Add API function to set an SC as active-tab
- [x] Move all lookup maps into ContainterWidget as non-static members, otherwise we can not have the same SC name inside another ContainerWidget instance.
The uniqueness of a SectionContainer needs to be restricted to its parent ContainerWidget, not global!
### Some day...
- [ ] Drop indicator images should be fully visible over the DropOverlay rectangle
- [ ] Pin contents: Pins a content and its title widget to the edge and opens on click/hover as long as it has focus
- [ ] API: Make it possible to use custom drop images
- [ ] API: Add possibility to make a SectionContent element floatable (`ContainerWidget::setFloating(SectionContent*)`?)
## Notes
- *SectionContent* class may safe a "size-type" property, which defines how the size of the widget should be handled.
- PerCent: Resize in proportion to other widgets.
- Fixed: Width or height are fixed (based on orientation).

View File

@@ -2,4 +2,5 @@ TEMPLATE = subdirs
SUBDIRS = \
AdvancedDockingSystem \
AdvancedDockingSystemDemo
AdvancedDockingSystemDemo \
AdvancedDockingSystemUnitTests