Converted project to C++.
200
.gitignore
vendored
@ -1,105 +1,117 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
## Linux/Windows annoying files
|
||||
*~
|
||||
|
||||
# C extensions
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
## C++
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
## Qt
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
# C++ objects and libs
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.a
|
||||
*.la
|
||||
*.lai
|
||||
*.so
|
||||
*.dll
|
||||
*.dylib
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
# Qt-es
|
||||
object_script.*.Release
|
||||
object_script.*.Debug
|
||||
*_plugin_import.cpp
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
*.pro.user
|
||||
*.pro.user.*
|
||||
*.qbs.user
|
||||
*.qbs.user.*
|
||||
*.moc
|
||||
moc_*.cpp
|
||||
moc_*.h
|
||||
qrc_*.cpp
|
||||
ui_*.h
|
||||
*.qmlc
|
||||
*.jsc
|
||||
Makefile*
|
||||
*build-*
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
# Qt unit tests
|
||||
target_wrapper.*
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
# QtCreator
|
||||
*.autosave
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
# QtCreator Qml
|
||||
*.qmlproject.user
|
||||
*.qmlproject.user.*
|
||||
|
||||
# QtCreator CMake
|
||||
CMakeLists.txt.user*
|
||||
|
55
Ember.pro
Normal file
@ -0,0 +1,55 @@
|
||||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2018-08-04T23:16:44
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
QT += core gui
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TARGET = Ember
|
||||
TEMPLATE = app
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any feature of Qt which has been marked as deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if you use deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
ui/welcome/welcomedialog.cpp \
|
||||
ui/dialogs/newprojectdialog.cpp \
|
||||
ui/mainwindow.cpp \
|
||||
model/project.cpp \
|
||||
model/projectitem.cpp \
|
||||
storage/appdatastorage.cpp \
|
||||
business/projectmanager.cpp
|
||||
|
||||
HEADERS += \
|
||||
ui/welcome/welcomedialog.h \
|
||||
ui/dialogs/newprojectdialog.h \
|
||||
ui/mainwindow.h \
|
||||
model/project.h \
|
||||
model/projectitem.h \
|
||||
properties/config.h \
|
||||
storage/appdatastorage.h \
|
||||
business/projectmanager.h
|
||||
|
||||
FORMS += \
|
||||
ui/welcome/welcomedialog.ui \
|
||||
ui/dialogs/newprojectdialog.ui
|
||||
|
||||
RESOURCES += \
|
||||
resources/appresources.qrc
|
||||
|
||||
LIBS += -lboost_filesystem
|
||||
LIBS += -lboost_system
|
||||
LIBS += -lpugixml
|
20
Makefile
@ -1,20 +0,0 @@
|
||||
RESOURCES=$(shell find . -name '*.qrc')
|
||||
RESOURCES_OUT=$(RESOURCES:.qrc=_rc.py)
|
||||
|
||||
UI=$(shell find . -name '*.ui')
|
||||
UI_OUT=$(UI:.ui=_ui.py)
|
||||
|
||||
all: $(RESOURCES_OUT) $(UI_OUT)
|
||||
|
||||
clean:
|
||||
rm -f ${UI_OUT}
|
||||
rm -f ${RESOURCES_OUT}
|
||||
find -name '__pycache__' -exec rm -rf "{}" \;
|
||||
|
||||
%_rc.py: %.qrc
|
||||
pyrcc5 $< -o $@
|
||||
|
||||
%_ui.py: %.ui
|
||||
pyuic5 $< -o $@
|
||||
|
||||
.PHONY: all clean
|
@ -1 +0,0 @@
|
||||
from .project_manager import ProjectManager
|
@ -1,90 +0,0 @@
|
||||
from typing import Iterable
|
||||
|
||||
from model.project import Project
|
||||
from model import Project, RecentProject
|
||||
from storage import AppDataStorage
|
||||
|
||||
class ProjectManager(object):
|
||||
|
||||
def __init__(self, appDataStorage : AppDataStorage):
|
||||
self.__appDataStorage = appDataStorage
|
||||
self.__recentProjects : dict = None
|
||||
|
||||
"""
|
||||
Gets a list of recent projects. The list is lazily loaded on the first call.
|
||||
|
||||
Returns:
|
||||
list of RecentProjects
|
||||
"""
|
||||
def getRecentProjects(self) -> Iterable[RecentProject]:
|
||||
if self.__recentProjects is None:
|
||||
self.__recentProjects = {}
|
||||
for item in self.__appDataStorage.readRecentProjects():
|
||||
self.__recentProjects[item.path] = item
|
||||
|
||||
return self.__recentProjects.values()
|
||||
|
||||
"""
|
||||
Pins a recent project.
|
||||
|
||||
Args:
|
||||
entry: recent project
|
||||
isPinned: pinned or not
|
||||
"""
|
||||
def pinRecentProject(self, entry : RecentProject, isPinned : bool = True):
|
||||
entry.pinned = isPinned
|
||||
self.__appDataStorage.writeRecentProjects(self.__recentProjects.values())
|
||||
|
||||
"""
|
||||
Deletes a recent project.
|
||||
|
||||
Args:
|
||||
entry: recent project
|
||||
"""
|
||||
def deleteRecentProject(self, entry : RecentProject):
|
||||
self.__recentProjects.pop(entry.path, None)
|
||||
self.__appDataStorage.writeRecentProjects(self.__recentProjects.values())
|
||||
|
||||
"""
|
||||
Creates a new project at the given path, and returns the created project.
|
||||
|
||||
Args:
|
||||
path: path to project file
|
||||
"""
|
||||
def newProject(self, path : str) -> Project:
|
||||
return Project(path)
|
||||
|
||||
"""
|
||||
Opens an existing project.
|
||||
|
||||
Args:
|
||||
path: path to project file
|
||||
"""
|
||||
def openProject(self, path : str) -> Project:
|
||||
return Project(path)
|
||||
|
||||
# def debug_populateRecentProjects(self):
|
||||
# self.__recentProjects['/home/tibi/Videos/project.pro'] = {
|
||||
# 'name' : 'Debug project',
|
||||
# 'path' : '/home/tibi/Videos/project.pro',
|
||||
# 'pinned' : True,
|
||||
# 'date' : 1
|
||||
# }
|
||||
# self.__recentProjects['/home/tibi/Videos/project2.pro'] = {
|
||||
# 'name' : 'Debug project 2',
|
||||
# 'path' : '/home/tibi/Videos/project2.pro',
|
||||
# 'pinned' : False,
|
||||
# 'date' : 2
|
||||
# }
|
||||
# self.__recentProjects['/home/tibi/Videos/project3.pro'] = {
|
||||
# 'name' : 'Debug project 3',
|
||||
# 'path' : '/home/tibi/Videos/project3.pro',
|
||||
# 'pinned' : False,
|
||||
# 'date' : 3
|
||||
# }
|
||||
# self.__recentProjects['/home/tibi/Videos/project4.pro'] = {
|
||||
# 'name' : 'Debug project 4',
|
||||
# 'path' : '/home/tibi/Videos/project4.pro',
|
||||
# 'pinned' : False,
|
||||
# 'date' : 4
|
||||
# }
|
11
business/projectmanager.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "projectmanager.h"
|
||||
|
||||
namespace Ember
|
||||
{
|
||||
|
||||
ProjectManager::ProjectManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
14
business/projectmanager.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PROJECTMANAGER_H
|
||||
#define PROJECTMANAGER_H
|
||||
|
||||
namespace Ember
|
||||
{
|
||||
|
||||
class ProjectManager
|
||||
{
|
||||
public:
|
||||
ProjectManager();
|
||||
};
|
||||
|
||||
}
|
||||
#endif // PROJECTMANAGER_H
|
9
dependencies.txt
Normal file
@ -0,0 +1,9 @@
|
||||
Runtime:
|
||||
libboost-filesystem1.65.1
|
||||
libboost-system
|
||||
libpugixml1v5
|
||||
|
||||
Build:
|
||||
libboost-filesystem-dev
|
||||
libboost-system-dev
|
||||
libpugixml-dev
|
14
import/Qt-Advanced-Docking-System-1.0.0/.astylerc
Normal 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
|
1
import/Qt-Advanced-Docking-System-1.0.0/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.pro.user
|
24
import/Qt-Advanced-Docking-System-1.0.0/.travis.yml
Normal file
@ -0,0 +1,24 @@
|
||||
language:
|
||||
- cpp
|
||||
|
||||
compiler:
|
||||
- g++
|
||||
|
||||
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
|
||||
|
||||
#- sudo apt-get install -qq qtbase5-dev qtdeclarative5-dev libqt5webkit5-dev libsqlite3-dev
|
||||
#- sudo apt-get install -qq qt5-default qttools5-dev-tools
|
@ -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
|
@ -0,0 +1,35 @@
|
||||
TARGET = AdvancedDockingSystem
|
||||
TEMPLATE = lib
|
||||
VERSION = 1.0.0
|
||||
|
||||
CONFIG += adsBuildShared
|
||||
|
||||
QT += core gui
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
greaterThan(QT_MAJOR_VERSION, 4): DEFINES += ADS_NAMESPACE_ENABLED
|
||||
|
||||
adsBuildShared {
|
||||
CONFIG += shared
|
||||
DEFINES += ADS_EXPORT
|
||||
}
|
||||
!adsBuildShared {
|
||||
CONFIG += staticlib
|
||||
}
|
||||
|
||||
INCLUDEPATH += $$PWD/include
|
||||
|
||||
windows {
|
||||
# MinGW
|
||||
*-g++* {
|
||||
QMAKE_CXXFLAGS += -std=c++11
|
||||
QMAKE_CXXFLAGS += -Wall -Wextra -pedantic
|
||||
}
|
||||
# MSVC
|
||||
*-msvc* {
|
||||
}
|
||||
}
|
||||
|
||||
RESOURCES += \
|
||||
res/ads.qrc
|
||||
|
||||
include(AdvancedDockingSystem.pri)
|
@ -0,0 +1,74 @@
|
||||
#ifndef ADS_API_H
|
||||
#define ADS_API_H
|
||||
|
||||
#include <QFlags>
|
||||
class QWidget;
|
||||
class QSplitter;
|
||||
|
||||
// DLL Export API
|
||||
#ifdef _WIN32
|
||||
#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)
|
||||
#endif
|
||||
#else
|
||||
#define ADS_EXPORT_API
|
||||
#endif
|
||||
|
||||
// Use namespace
|
||||
// Disabled with Qt4, it makes problems with signals and slots.
|
||||
#ifdef ADS_NAMESPACE_ENABLED
|
||||
#define ADS_NAMESPACE_BEGIN namespace ads {
|
||||
#define ADS_NAMESPACE_END }
|
||||
#define ADS_NS ::ads
|
||||
#else
|
||||
#define ADS_NAMESPACE_BEGIN
|
||||
#define ADS_NAMESPACE_END
|
||||
#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
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
class ContainerWidget;
|
||||
class SectionWidget;
|
||||
|
||||
enum DropArea
|
||||
{
|
||||
InvalidDropArea = 0,
|
||||
TopDropArea = 1,
|
||||
RightDropArea = 2,
|
||||
BottomDropArea = 4,
|
||||
LeftDropArea = 8,
|
||||
CenterDropArea = 16,
|
||||
|
||||
AllAreas = TopDropArea | RightDropArea | BottomDropArea | LeftDropArea | CenterDropArea
|
||||
};
|
||||
Q_DECLARE_FLAGS(DropAreas, DropArea)
|
||||
|
||||
void deleteEmptySplitter(ContainerWidget* container);
|
||||
ContainerWidget* findParentContainerWidget(QWidget* w);
|
||||
SectionWidget* findParentSectionWidget(QWidget* w);
|
||||
QSplitter* findParentSplitter(QWidget* w);
|
||||
QSplitter* findImmediateSplitter(QWidget* w);
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
@ -0,0 +1,191 @@
|
||||
#ifndef ADS_CONTAINERWIDGET_H
|
||||
#define ADS_CONTAINERWIDGET_H
|
||||
|
||||
#include <QList>
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
#include <QFrame>
|
||||
class QPoint;
|
||||
class QSplitter;
|
||||
class QMenu;
|
||||
class QGridLayout;
|
||||
|
||||
#include "ads/API.h"
|
||||
#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;
|
||||
|
||||
|
||||
/*!
|
||||
* ContainerWidget is the main container to provide the docking
|
||||
* functionality. It manages multiple sections with all possible areas.
|
||||
*/
|
||||
class ADS_EXPORT_API ContainerWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class SectionContent;
|
||||
friend class SectionWidget;
|
||||
friend class FloatingWidget;
|
||||
friend class SectionTitleWidget;
|
||||
friend class SectionContentWidget;
|
||||
|
||||
public:
|
||||
explicit ContainerWidget(QWidget *parent = NULL);
|
||||
virtual ~ContainerWidget();
|
||||
|
||||
//
|
||||
// Public API
|
||||
//
|
||||
|
||||
/*!
|
||||
* 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.
|
||||
* Returns a pointer to the SectionWidget of the added SectionContent. Do not use it for anything else than adding more
|
||||
* SectionContent elements with this method.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
bool showSectionContent(const SectionContent::RefPtr& sc);
|
||||
|
||||
/*!
|
||||
* Closes the specified SectionContent from UI.
|
||||
* Independed of the current state, whether it is used inside a section or is floating.
|
||||
*/
|
||||
bool hideSectionContent(const SectionContent::RefPtr& sc);
|
||||
|
||||
/*!
|
||||
* Selects the specific SectionContent as current, if it is part of a SectionWidget.
|
||||
* If SC is floating, it does nothing (or should we show it?)
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
QMenu* createContextMenu() const;
|
||||
|
||||
/*!
|
||||
* Serializes the current state of contents and returns it as a plain byte array.
|
||||
* \see restoreState(const QByteArray&)
|
||||
*/
|
||||
QByteArray saveState() const;
|
||||
|
||||
/*!
|
||||
* Deserilizes the state of contents from <em>data</em>, which was written with <em>saveState()</em>.
|
||||
* \see saveState()
|
||||
*/
|
||||
bool restoreState(const QByteArray& data);
|
||||
|
||||
//
|
||||
// Advanced Public API
|
||||
// You usually should not need access to this methods
|
||||
//
|
||||
|
||||
// Outer DropAreas
|
||||
QRect outerTopDropRect() const;
|
||||
QRect outerRightDropRect() const;
|
||||
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
|
||||
//
|
||||
|
||||
SectionWidget* newSectionWidget();
|
||||
SectionWidget* dropContent(const InternalContentData& data, SectionWidget* targetSection, DropArea area, bool autoActive = true);
|
||||
void addSection(SectionWidget* section);
|
||||
SectionWidget* sectionAt(const QPoint& pos) const;
|
||||
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);
|
||||
|
||||
bool takeContent(const SectionContent::RefPtr& sc, InternalContentData& data);
|
||||
|
||||
private slots:
|
||||
void onActiveTabChanged();
|
||||
void onActionToggleSectionContentVisibility(bool visible);
|
||||
|
||||
signals:
|
||||
void orientationChanged();
|
||||
|
||||
/*!
|
||||
* Emits whenever the "isActiveTab" state of a SectionContent changes.
|
||||
* Whenever the users sets another tab as active, this signal gets invoked
|
||||
* for the old tab and the new active tab (the order is unspecified).
|
||||
*/
|
||||
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;
|
||||
QList<FloatingWidget*> _floatings;
|
||||
QHash<int, HiddenSectionItem> _hiddenSectionContents;
|
||||
|
||||
|
||||
// Helper lookup maps, restricted to this container.
|
||||
QHash<int, SectionContent::WeakPtr> _scLookupMapById;
|
||||
QHash<QString, SectionContent::WeakPtr> _scLookupMapByName;
|
||||
QHash<int, SectionWidget*> _swLookupMapById;
|
||||
|
||||
|
||||
// Layout stuff
|
||||
QGridLayout* _mainLayout;
|
||||
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
|
||||
#endif
|
@ -0,0 +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 DropOverlayCross;
|
||||
|
||||
/*!
|
||||
* 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(QWidget* parent);
|
||||
virtual ~DropOverlay();
|
||||
|
||||
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:
|
||||
DropAreas _allowedAreas;
|
||||
DropOverlayCross* _cross;
|
||||
|
||||
bool _fullAreaDrop;
|
||||
QPointer<QWidget> _target;
|
||||
QRect _targetRect;
|
||||
DropArea _lastLocation;
|
||||
};
|
||||
|
||||
/*!
|
||||
* 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:
|
||||
DropOverlayCross(DropOverlay* overlay);
|
||||
virtual ~DropOverlayCross();
|
||||
|
||||
void setAreaWidgets(const QHash<DropArea, QWidget*>& widgets);
|
||||
DropArea cursorLocation() const;
|
||||
|
||||
protected:
|
||||
virtual void showEvent(QShowEvent* e);
|
||||
|
||||
private:
|
||||
void reset();
|
||||
|
||||
private:
|
||||
DropOverlay* _overlay;
|
||||
QHash<DropArea, QWidget*> _widgets;
|
||||
QGridLayout* _grid;
|
||||
};
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
@ -0,0 +1,46 @@
|
||||
#ifndef FLOATINGWIDGET_H
|
||||
#define FLOATINGWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
class QBoxLayout;
|
||||
|
||||
#include "ads/API.h"
|
||||
#include "ads/SectionContent.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
class ContainerWidget;
|
||||
class SectionTitleWidget;
|
||||
class SectionContentWidget;
|
||||
class InternalContentData;
|
||||
|
||||
// FloatingWidget holds and displays SectionContent as a floating window.
|
||||
// It can be resized, moved and dropped back into a SectionWidget.
|
||||
class FloatingWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class ContainerWidget;
|
||||
|
||||
public:
|
||||
FloatingWidget(ContainerWidget* container, SectionContent::RefPtr sc, SectionTitleWidget* titleWidget, SectionContentWidget* contentWidget, QWidget* parent = NULL);
|
||||
virtual ~FloatingWidget();
|
||||
|
||||
SectionContent::RefPtr content() const { return _content; }
|
||||
|
||||
public://private:
|
||||
bool takeContent(InternalContentData& data);
|
||||
|
||||
private slots:
|
||||
void onCloseButtonClicked();
|
||||
|
||||
private:
|
||||
ContainerWidget* _container;
|
||||
SectionContent::RefPtr _content;
|
||||
SectionTitleWidget* _titleWidget;
|
||||
SectionContentWidget* _contentWidget;
|
||||
|
||||
QBoxLayout* _titleLayout;
|
||||
};
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
@ -0,0 +1,54 @@
|
||||
#ifndef ADS_INTERNAL_HEADER
|
||||
#define ADS_INTERNAL_HEADER
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <QWeakPointer>
|
||||
|
||||
#include "ads/API.h"
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
#else
|
||||
#include "ads/SectionContent.h"
|
||||
#endif
|
||||
|
||||
#define SCLookupMapById(X) X->_scLookupMapById
|
||||
#define SCLookupMapByName(X) X->_scLookupMapByName
|
||||
#define SWLookupMapById(X) X->_swLookupMapById
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
class SectionContent;
|
||||
class SectionTitleWidget;
|
||||
class SectionContentWidget;
|
||||
|
||||
|
||||
class InternalContentData
|
||||
{
|
||||
public:
|
||||
typedef QSharedPointer<InternalContentData> RefPtr;
|
||||
typedef QWeakPointer<InternalContentData> WeakPtr;
|
||||
|
||||
InternalContentData();
|
||||
~InternalContentData();
|
||||
|
||||
QSharedPointer<SectionContent> content;
|
||||
SectionTitleWidget* titleWidget;
|
||||
SectionContentWidget* contentWidget;
|
||||
};
|
||||
|
||||
|
||||
class HiddenSectionItem
|
||||
{
|
||||
public:
|
||||
HiddenSectionItem() :
|
||||
preferredSectionId(-1),
|
||||
preferredSectionIndex(-1)
|
||||
{}
|
||||
|
||||
int preferredSectionId;
|
||||
int preferredSectionIndex;
|
||||
InternalContentData data;
|
||||
};
|
||||
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
@ -0,0 +1,78 @@
|
||||
#ifndef SECTIONCONTENT_H
|
||||
#define SECTIONCONTENT_H
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <QWeakPointer>
|
||||
#include <QPointer>
|
||||
class QWidget;
|
||||
|
||||
#include "ads/API.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
class ContainerWidget;
|
||||
|
||||
class ADS_EXPORT_API SectionContent
|
||||
{
|
||||
friend class ContainerWidget;
|
||||
|
||||
private:
|
||||
SectionContent();
|
||||
SectionContent(const SectionContent&);
|
||||
SectionContent& operator=(const SectionContent&);
|
||||
|
||||
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.
|
||||
* \param uniqueName An unique identifier across the entire process.
|
||||
* \param container The parent ContainerWidget in which this content will be active.
|
||||
* \param title The widget to use as title.
|
||||
* \param content The widget to use as content.
|
||||
* \return May return a invalid ref-pointer in case of invalid parameters.
|
||||
*/
|
||||
static RefPtr newSectionContent(const QString& uniqueName, ContainerWidget* container, QWidget* title, QWidget* content);
|
||||
|
||||
virtual ~SectionContent();
|
||||
int uid() const;
|
||||
QString uniqueName() const;
|
||||
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;
|
||||
QString _uniqueName;
|
||||
|
||||
QPointer<ContainerWidget> _containerWidget;
|
||||
QPointer<QWidget> _titleWidget;
|
||||
QPointer<QWidget> _contentWidget;
|
||||
|
||||
// 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();
|
||||
};
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
@ -0,0 +1,28 @@
|
||||
#ifndef SECTION_CONTENT_WIDGET_H
|
||||
#define SECTION_CONTENT_WIDGET_H
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
#include "ads/API.h"
|
||||
#include "ads/SectionContent.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
class ContainerWidget;
|
||||
class SectionWidget;
|
||||
|
||||
class SectionContentWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class ContainerWidget;
|
||||
|
||||
public:
|
||||
SectionContentWidget(SectionContent::RefPtr c, QWidget* parent = 0);
|
||||
virtual ~SectionContentWidget();
|
||||
|
||||
private:
|
||||
SectionContent::RefPtr _content;
|
||||
};
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
@ -0,0 +1,54 @@
|
||||
#ifndef SECTION_TITLE_WIDGET_H
|
||||
#define SECTION_TITLE_WIDGET_H
|
||||
|
||||
#include <QPointer>
|
||||
#include <QPoint>
|
||||
#include <QFrame>
|
||||
|
||||
#include "ads/API.h"
|
||||
#include "ads/SectionContent.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
class ContainerWidget;
|
||||
class SectionWidget;
|
||||
class FloatingWidget;
|
||||
|
||||
class SectionTitleWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool activeTab READ isActiveTab WRITE setActiveTab NOTIFY activeTabChanged)
|
||||
|
||||
friend class ContainerWidget;
|
||||
friend class SectionWidget;
|
||||
|
||||
SectionContent::RefPtr _content;
|
||||
|
||||
// Drag & Drop (Floating)
|
||||
QPointer<FloatingWidget> _fw;
|
||||
QPoint _dragStartPos;
|
||||
|
||||
// Drag & Drop (Title/Tabs)
|
||||
bool _tabMoving;
|
||||
|
||||
// Property values
|
||||
bool _activeTab;
|
||||
|
||||
public:
|
||||
SectionTitleWidget(SectionContent::RefPtr content, QWidget* parent);
|
||||
virtual ~SectionTitleWidget();
|
||||
|
||||
bool isActiveTab() const;
|
||||
void setActiveTab(bool active);
|
||||
|
||||
protected:
|
||||
virtual void mousePressEvent(QMouseEvent* ev);
|
||||
virtual void mouseReleaseEvent(QMouseEvent* ev);
|
||||
virtual void mouseMoveEvent(QMouseEvent* ev);
|
||||
|
||||
signals:
|
||||
void activeTabChanged();
|
||||
void clicked();
|
||||
};
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
@ -0,0 +1,103 @@
|
||||
#ifndef SECTION_WIDGET_H
|
||||
#define SECTION_WIDGET_H
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPointer>
|
||||
#include <QList>
|
||||
#include <QFrame>
|
||||
#include <QScrollArea>
|
||||
class QBoxLayout;
|
||||
class QStackedLayout;
|
||||
class QPushButton;
|
||||
class QMenu;
|
||||
|
||||
#include "ads/API.h"
|
||||
#include "ads/Internal.h"
|
||||
#include "ads/SectionContent.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
class ContainerWidget;
|
||||
class SectionTitleWidget;
|
||||
class SectionContentWidget;
|
||||
|
||||
// SectionWidget manages multiple instances of SectionContent.
|
||||
// It displays a title TAB, which is clickable and will switch to
|
||||
// the contents associated to the title when clicked.
|
||||
class ADS_EXPORT_API SectionWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
friend class ContainerWidget;
|
||||
|
||||
explicit SectionWidget(ContainerWidget* parent);
|
||||
|
||||
public:
|
||||
virtual ~SectionWidget();
|
||||
|
||||
int uid() const;
|
||||
ContainerWidget* containerWidget() const;
|
||||
|
||||
QRect titleAreaGeometry() const;
|
||||
QRect contentAreaGeometry() const;
|
||||
|
||||
const QList<SectionContent::RefPtr>& contents() const { return _contents; }
|
||||
void addContent(const SectionContent::RefPtr& c);
|
||||
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;
|
||||
|
||||
QPointer<ContainerWidget> _container;
|
||||
QList<SectionContent::RefPtr> _contents;
|
||||
QList<SectionTitleWidget*> _sectionTitles;
|
||||
QList<SectionContentWidget*> _sectionContents;
|
||||
|
||||
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;
|
||||
SectionContent::RefPtr _mousePressContent;
|
||||
SectionTitleWidget* _mousePressTitleWidget;
|
||||
|
||||
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
|
@ -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
|
@ -0,0 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/ads">
|
||||
<file>stylesheets/default-windows.css</file>
|
||||
<file>stylesheets/vendor-partsolutions.css</file>
|
||||
<file>stylesheets/modern-windows.css</file>
|
||||
</qresource>
|
||||
</RCC>
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Default style sheet on Windows Platforms
|
||||
* Note: Always use CSS-classes with and without "ads--" namespace to support Qt4 & Qt5
|
||||
*/
|
||||
|
||||
ads--ContainerWidget,
|
||||
ContainerWidget
|
||||
{
|
||||
background: palette(dark);
|
||||
}
|
||||
|
||||
ads--ContainerWidget QSplitter::handle,
|
||||
ContainerWidget QSplitter::handle
|
||||
{
|
||||
background: palette(dark);
|
||||
}
|
||||
|
||||
ads--SectionWidget,
|
||||
SectionWidget
|
||||
{
|
||||
background: palette(window);
|
||||
border: 1px solid palette(light);
|
||||
}
|
||||
|
||||
ads--SectionWidget #tabsMenuButton::menu-indicator,
|
||||
SectionWidget #tabsMenuButton::menu-indicator
|
||||
{
|
||||
image: none;
|
||||
}
|
||||
|
||||
ads--SectionTitleWidget,
|
||||
SectionTitleWidget
|
||||
{
|
||||
background: palette(window);
|
||||
border-color: palette(light);
|
||||
border-style: solid;
|
||||
border-width: 0 1px 0 0;
|
||||
padding: 0 9px;
|
||||
}
|
||||
|
||||
ads--SectionTitleWidget[activeTab="true"],
|
||||
SectionTitleWidget[activeTab="true"]
|
||||
{
|
||||
/* background: palette(light);*/
|
||||
/* background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 255), stop:1 rgba(240, 240, 240, 255));*/
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:0.5, stop:0 palette(window), stop:1 palette(light));
|
||||
}
|
||||
|
||||
ads--SectionContentWidget,
|
||||
SectionContentWidget
|
||||
{
|
||||
background: palette(light);
|
||||
border-color: palette(light);
|
||||
border-style: solid;
|
||||
border-width: 1px 0 0 0;
|
||||
}
|
||||
|
||||
/* Special: QLabels inside SectionTitleWidget
|
||||
*/
|
||||
ads--SectionTitleWidget QLabel,
|
||||
SectionTitleWidget QLabel
|
||||
{
|
||||
color: palette(dark);
|
||||
}
|
||||
ads--SectionTitleWidget[activeTab="true"] QLabel,
|
||||
SectionTitleWidget[activeTab="true"] QLabel
|
||||
{
|
||||
color: palette(foreground);
|
||||
}
|
||||
|
||||
/* Special: QLabels inside SectionTitleWidget, which is floating
|
||||
*/
|
||||
ads--FloatingWidget ads--SectionTitleWidget QLabel,
|
||||
FloatingWidget SectionTitleWidget QLabel
|
||||
{
|
||||
color: palette(foreground);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
QSplitter::handle {
|
||||
background: palette(light);
|
||||
}
|
||||
|
||||
ads--ContainerWidget, ContainerWidget {
|
||||
background: palette(light);
|
||||
}
|
||||
|
||||
ads--SectionWidget, SectionWidget {
|
||||
background: palette(light);
|
||||
}
|
||||
|
||||
ads--SectionTitleWidget, SectionTitleWidget {
|
||||
background: #ffffff;
|
||||
}
|
||||
ads--SectionTitleWidget QLabel, SectionTitleWidget QLabel {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
ads--SectionTitleWidget[activeTab="true"], SectionTitleWidget[activeTab="true"] {
|
||||
background: #000000;
|
||||
border-right: 1px solid #000000;
|
||||
padding: 9px;
|
||||
}
|
||||
ads--SectionTitleWidget[activeTab="true"] QLabel, SectionTitleWidget[activeTab="true"] QLabel {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
ads--SectionContentWidget, SectionContentWidget {
|
||||
border: 1px solid #000000;
|
||||
}
|
||||
|
||||
QAbstractItemView {
|
||||
border: 0;
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Style sheet used by CADENAS PARTsolutions product line
|
||||
* Requires Qt4 compatibility
|
||||
*/
|
||||
|
||||
QSplitter::handle:vertical {
|
||||
image: url(:/img/splitter-horizontal.png);
|
||||
}
|
||||
|
||||
QSplitter::handle:horizontal {
|
||||
image: url(:/img/splitter-vertical.png);
|
||||
}
|
||||
|
||||
ads--ContainerWidget, ContainerWidget {
|
||||
background: #9ab6ca;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
ads--SectionWidget, SectionWidget {
|
||||
background: #7c9eb3;
|
||||
border-color: #ffffff;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
ads--SectionTitleWidget, SectionTitleWidget {
|
||||
background: #7c9eb3;
|
||||
border-right: 1px solid #E7F3F8;
|
||||
padding: 6px 6px;
|
||||
}
|
||||
|
||||
ads--SectionTitleWidget[activeTab="true"], SectionTitleWidget[activeTab="true"] {
|
||||
background: #E7F3F8;
|
||||
border: 1px solid #E7F3F8;
|
||||
}
|
||||
|
||||
ads--SectionWidget QPushButton#closeButton, SectionWidget QPushButton#closeButton,
|
||||
ads--FloatingWidget QPushButton#closeButton, FloatingWidget QPushButton#closeButton {
|
||||
background: #ff0000;
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
ads--SectionContentWidget, SectionContentWidget {
|
||||
background: #ffffff;
|
||||
border: 0px solid #E7F3F8;
|
||||
}
|
||||
|
||||
/* Special */
|
||||
|
||||
IconTitleWidget {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ads--SectionTitleWidget QLabel, SectionTitleWidget QLabel {
|
||||
color: #ffffff;
|
||||
background: #7c9eb3;
|
||||
}
|
||||
|
||||
ads--SectionTitleWidget[activeTab="true"] QLabel, SectionTitleWidget[activeTab="true"] QLabel {
|
||||
color: #000000;
|
||||
background: #E7F3F8;
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
#include "ads/API.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSplitter>
|
||||
#include <QLayout>
|
||||
#include <QVariant>
|
||||
|
||||
#include "ads/ContainerWidget.h"
|
||||
#include "ads/SectionWidget.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
static bool splitterContainsSectionWidget(QSplitter* splitter)
|
||||
{
|
||||
for (int i = 0; i < splitter->count(); ++i)
|
||||
{
|
||||
QWidget* w = splitter->widget(i);
|
||||
QSplitter* sp = qobject_cast<QSplitter*>(w);
|
||||
SectionWidget* sw = NULL;
|
||||
if (sp && splitterContainsSectionWidget(sp))
|
||||
return true;
|
||||
else if ((sw = qobject_cast<SectionWidget*>(w)) != NULL)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void deleteEmptySplitter(ContainerWidget* container)
|
||||
{
|
||||
bool doAgain = false;
|
||||
do
|
||||
{
|
||||
doAgain = false;
|
||||
QList<QSplitter*> splitters = container->findChildren<QSplitter*>();
|
||||
for (int i = 0; i < splitters.count(); ++i)
|
||||
{
|
||||
QSplitter* sp = splitters.at(i);
|
||||
if (!sp->property("ads-splitter").toBool())
|
||||
continue;
|
||||
if (sp->count() > 0 && splitterContainsSectionWidget(sp))
|
||||
continue;
|
||||
delete splitters[i];
|
||||
doAgain = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (doAgain);
|
||||
}
|
||||
|
||||
ContainerWidget* findParentContainerWidget(QWidget* w)
|
||||
{
|
||||
ContainerWidget* cw = 0;
|
||||
QWidget* next = w;
|
||||
do
|
||||
{
|
||||
if ((cw = dynamic_cast<ContainerWidget*>(next)) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
next = next->parentWidget();
|
||||
}
|
||||
while (next);
|
||||
return cw;
|
||||
}
|
||||
|
||||
SectionWidget* findParentSectionWidget(class QWidget* w)
|
||||
{
|
||||
SectionWidget* cw = 0;
|
||||
QWidget* next = w;
|
||||
do
|
||||
{
|
||||
if ((cw = dynamic_cast<SectionWidget*>(next)) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
next = next->parentWidget();
|
||||
}
|
||||
while (next);
|
||||
return cw;
|
||||
}
|
||||
|
||||
QSplitter* findParentSplitter(class QWidget* w)
|
||||
{
|
||||
QSplitter* cw = 0;
|
||||
QWidget* next = w;
|
||||
do
|
||||
{
|
||||
if ((cw = dynamic_cast<QSplitter*>(next)) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
next = next->parentWidget();
|
||||
}
|
||||
while (next);
|
||||
return cw;
|
||||
}
|
||||
|
||||
QSplitter* findImmediateSplitter(class QWidget* w)
|
||||
{
|
||||
QSplitter* sp = NULL;
|
||||
QLayout* l = w->layout();
|
||||
if (!l || l->count() <= 0)
|
||||
return sp;
|
||||
for (int i = 0; i < l->count(); ++i)
|
||||
{
|
||||
QLayoutItem* li = l->itemAt(0);
|
||||
if (!li->widget())
|
||||
continue;
|
||||
if ((sp = dynamic_cast<QSplitter*>(li->widget())) != NULL)
|
||||
break;
|
||||
}
|
||||
return sp;
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
@ -0,0 +1,441 @@
|
||||
#include "ads/DropOverlay.h"
|
||||
|
||||
#include <QPointer>
|
||||
#include <QPaintEvent>
|
||||
#include <QResizeEvent>
|
||||
#include <QMoveEvent>
|
||||
#include <QPainter>
|
||||
#include <QGridLayout>
|
||||
#include <QCursor>
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
// Helper /////////////////////////////////////////////////////////////
|
||||
|
||||
static QPixmap createDropIndicatorPixmap(const QPalette& pal, const QSizeF& size, DropArea dropArea)
|
||||
{
|
||||
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(QWidget* parent) :
|
||||
QFrame(parent),
|
||||
_allowedAreas(InvalidDropArea),
|
||||
_cross(new DropOverlayCross(this)),
|
||||
_fullAreaDrop(false),
|
||||
_lastLocation(InvalidDropArea)
|
||||
{
|
||||
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||
setWindowOpacity(0.2);
|
||||
setWindowTitle("DropOverlay");
|
||||
|
||||
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
l->setSpacing(0);
|
||||
setLayout(l);
|
||||
|
||||
// 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()
|
||||
{
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return _cross->cursorLocation();
|
||||
}
|
||||
|
||||
DropArea DropOverlay::showDropOverlay(QWidget* target)
|
||||
{
|
||||
if (_target == target)
|
||||
{
|
||||
// Hint: We could update geometry of overlay here.
|
||||
DropArea da = cursorLocation();
|
||||
if (da != _lastLocation)
|
||||
{
|
||||
repaint();
|
||||
_lastLocation = da;
|
||||
}
|
||||
return da;
|
||||
}
|
||||
|
||||
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(areaColor, Qt::Dense4Pattern));
|
||||
p.setBrush(QBrush(areaColor));
|
||||
p.drawRect(r);
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw rect based on location
|
||||
QRect r = rect();
|
||||
const DropArea da = cursorLocation();
|
||||
switch (da)
|
||||
{
|
||||
case ADS_NS::TopDropArea:
|
||||
r.setHeight(r.height() / 2);
|
||||
break;
|
||||
case ADS_NS::RightDropArea:
|
||||
r.setX(r.width() / 2);
|
||||
break;
|
||||
case ADS_NS::BottomDropArea:
|
||||
r.setY(r.height() / 2);
|
||||
break;
|
||||
case ADS_NS::LeftDropArea:
|
||||
r.setWidth(r.width() / 2);
|
||||
break;
|
||||
case ADS_NS::CenterDropArea:
|
||||
r = rect();
|
||||
break;
|
||||
default:
|
||||
r = QRect();
|
||||
}
|
||||
if (!r.isNull())
|
||||
{
|
||||
p.fillRect(r, QBrush(areaColor, Qt::Dense4Pattern));
|
||||
p.setBrush(QBrush(areaColor));
|
||||
p.drawRect(r);
|
||||
}
|
||||
|
||||
// Draw rect over the entire size + border.
|
||||
// auto r = rect();
|
||||
// r.setWidth(r.width() - 1);
|
||||
// r.setHeight(r.height() - 1);
|
||||
|
||||
// p.fillRect(r, QBrush(QColor(0, 100, 255), Qt::Dense4Pattern));
|
||||
// p.setBrush(QBrush(QColor(0, 100, 255)));
|
||||
// p.drawRect(r);
|
||||
}
|
||||
|
||||
void DropOverlay::showEvent(QShowEvent*)
|
||||
{
|
||||
_cross->show();
|
||||
}
|
||||
|
||||
void DropOverlay::hideEvent(QHideEvent*)
|
||||
{
|
||||
_cross->hide();
|
||||
}
|
||||
|
||||
void DropOverlay::resizeEvent(QResizeEvent* e)
|
||||
{
|
||||
_cross->resize(e->size());
|
||||
}
|
||||
|
||||
void DropOverlay::moveEvent(QMoveEvent* e)
|
||||
{
|
||||
_cross->move(e->pos());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
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("DropOverlayCross");
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
|
||||
_grid = new QGridLayout();
|
||||
_grid->setContentsMargins(0, 0, 0, 0);
|
||||
_grid->setSpacing(6);
|
||||
|
||||
QBoxLayout* bl1 = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
bl1->setContentsMargins(0, 0, 0, 0);
|
||||
bl1->setSpacing(0);
|
||||
setLayout(bl1);
|
||||
|
||||
QBoxLayout* bl2 = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
bl2->setContentsMargins(0, 0, 0, 0);
|
||||
bl2->setSpacing(0);
|
||||
|
||||
bl1->addStretch(1);
|
||||
bl1->addLayout(bl2);
|
||||
bl2->addStretch(1);
|
||||
bl2->addLayout(_grid, 0);
|
||||
bl2->addStretch(1);
|
||||
bl1->addStretch(1);
|
||||
}
|
||||
|
||||
DropOverlayCross::~DropOverlayCross()
|
||||
{
|
||||
}
|
||||
|
||||
void DropOverlayCross::setAreaWidgets(const QHash<DropArea, QWidget*>& widgets)
|
||||
{
|
||||
// Delete old widgets.
|
||||
QMutableHashIterator<DropArea, QWidget*> i(_widgets);
|
||||
while (i.hasNext())
|
||||
{
|
||||
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))
|
||||
{
|
||||
return i.key();
|
||||
}
|
||||
}
|
||||
return InvalidDropArea;
|
||||
}
|
||||
|
||||
void DropOverlayCross::showEvent(QShowEvent*)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
w->setVisible(allowedAreas.testFlag(allAreas.at(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
@ -0,0 +1,84 @@
|
||||
#include "ads/FloatingWidget.h"
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QSizePolicy>
|
||||
#include <QMouseEvent>
|
||||
#include <QStyle>
|
||||
|
||||
#include "ads/ContainerWidget.h"
|
||||
#include "ads/SectionTitleWidget.h"
|
||||
#include "ads/SectionContentWidget.h"
|
||||
#include "ads/Internal.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
FloatingWidget::FloatingWidget(ContainerWidget* container, SectionContent::RefPtr sc, SectionTitleWidget* titleWidget, SectionContentWidget* contentWidget, QWidget* parent) :
|
||||
QWidget(parent, Qt::CustomizeWindowHint | Qt::Tool),
|
||||
_container(container),
|
||||
_content(sc),
|
||||
_titleWidget(titleWidget),
|
||||
_contentWidget(contentWidget)
|
||||
{
|
||||
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
l->setSpacing(0);
|
||||
setLayout(l);
|
||||
|
||||
// Title + Controls
|
||||
_titleLayout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
_titleLayout->addWidget(titleWidget, 1);
|
||||
l->addLayout(_titleLayout, 0);
|
||||
titleWidget->setActiveTab(false);
|
||||
|
||||
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);
|
||||
contentWidget->show();
|
||||
|
||||
// _container->_floatingWidgets.append(this);
|
||||
}
|
||||
|
||||
FloatingWidget::~FloatingWidget()
|
||||
{
|
||||
_container->_floatings.removeAll(this); // Note: I don't like this here, but we have to remove it from list...
|
||||
}
|
||||
|
||||
bool FloatingWidget::takeContent(InternalContentData& data)
|
||||
{
|
||||
data.content = _content;
|
||||
data.titleWidget = _titleWidget;
|
||||
data.contentWidget = _contentWidget;
|
||||
|
||||
_titleLayout->removeWidget(_titleWidget);
|
||||
_titleWidget->setParent(_container);
|
||||
_titleWidget = NULL;
|
||||
|
||||
layout()->removeWidget(_contentWidget);
|
||||
_contentWidget->setParent(_container);
|
||||
_contentWidget = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FloatingWidget::onCloseButtonClicked()
|
||||
{
|
||||
_container->hideSectionContent(_content);
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
@ -0,0 +1,15 @@
|
||||
#include "ads/Internal.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
InternalContentData::InternalContentData() :
|
||||
titleWidget(NULL),
|
||||
contentWidget(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
InternalContentData::~InternalContentData()
|
||||
{
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
@ -0,0 +1,115 @@
|
||||
#include "ads/SectionContent.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
|
||||
#include "ads/Internal.h"
|
||||
#include "ads/ContainerWidget.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
SectionContent::SectionContent() :
|
||||
_uid(GetNextUid()),
|
||||
_flags(AllFlags)
|
||||
{
|
||||
}
|
||||
|
||||
SectionContent::RefPtr SectionContent::newSectionContent(const QString& uniqueName, ContainerWidget* container, QWidget* title, QWidget* content)
|
||||
{
|
||||
if (uniqueName.isEmpty())
|
||||
{
|
||||
qFatal("Can not create SectionContent with empty uniqueName");
|
||||
return RefPtr();
|
||||
}
|
||||
else if (SCLookupMapByName(container).contains(uniqueName))
|
||||
{
|
||||
qFatal("Can not create SectionContent with already used uniqueName");
|
||||
return RefPtr();
|
||||
}
|
||||
else if (!container || !title || !content)
|
||||
{
|
||||
qFatal("Can not create SectionContent with NULL values");
|
||||
return RefPtr();
|
||||
}
|
||||
|
||||
QSharedPointer<SectionContent> sc(new SectionContent());
|
||||
sc->_uniqueName = uniqueName;
|
||||
sc->_containerWidget = container;
|
||||
sc->_titleWidget = title;
|
||||
sc->_contentWidget = content;
|
||||
|
||||
SCLookupMapById(container).insert(sc->uid(), sc);
|
||||
SCLookupMapByName(container).insert(sc->uniqueName(), sc);
|
||||
return sc;
|
||||
}
|
||||
|
||||
SectionContent::~SectionContent()
|
||||
{
|
||||
if (_containerWidget)
|
||||
{
|
||||
SCLookupMapById(_containerWidget).remove(_uid);
|
||||
SCLookupMapByName(_containerWidget).remove(_uniqueName);
|
||||
}
|
||||
delete _titleWidget;
|
||||
delete _contentWidget;
|
||||
}
|
||||
|
||||
int SectionContent::uid() const
|
||||
{
|
||||
return _uid;
|
||||
}
|
||||
|
||||
QString SectionContent::uniqueName() const
|
||||
{
|
||||
return _uniqueName;
|
||||
}
|
||||
|
||||
ContainerWidget* SectionContent::containerWidget() const
|
||||
{
|
||||
return _containerWidget;
|
||||
}
|
||||
|
||||
QWidget* SectionContent::titleWidget() const
|
||||
{
|
||||
return _titleWidget;
|
||||
}
|
||||
|
||||
QWidget* SectionContent::contentWidget() const
|
||||
{
|
||||
return _contentWidget;
|
||||
}
|
||||
|
||||
SectionContent::Flags SectionContent::flags() const
|
||||
{
|
||||
return _flags;
|
||||
}
|
||||
|
||||
QString SectionContent::visibleTitle() const
|
||||
{
|
||||
if (_title.isEmpty())
|
||||
return _uniqueName;
|
||||
return _title;
|
||||
}
|
||||
|
||||
QString SectionContent::title() const
|
||||
{
|
||||
return _title;
|
||||
}
|
||||
|
||||
void SectionContent::setTitle(const QString& title)
|
||||
{
|
||||
_title = title;
|
||||
}
|
||||
|
||||
void SectionContent::setFlags(const Flags f)
|
||||
{
|
||||
_flags = f;
|
||||
}
|
||||
|
||||
int SectionContent::GetNextUid()
|
||||
{
|
||||
static int NextUid = 0;
|
||||
return ++NextUid;
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
@ -0,0 +1,23 @@
|
||||
#include "ads/SectionContentWidget.h"
|
||||
|
||||
#include <QBoxLayout>
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
SectionContentWidget::SectionContentWidget(SectionContent::RefPtr c, QWidget* parent) :
|
||||
QFrame(parent),
|
||||
_content(c)
|
||||
{
|
||||
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
l->setSpacing(0);
|
||||
l->addWidget(_content->contentWidget());
|
||||
setLayout(l);
|
||||
}
|
||||
|
||||
SectionContentWidget::~SectionContentWidget()
|
||||
{
|
||||
layout()->removeWidget(_content->contentWidget());
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
@ -0,0 +1,291 @@
|
||||
#include "ads/SectionTitleWidget.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QApplication>
|
||||
#include <QBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
#include <QMimeData>
|
||||
#include <QDrag>
|
||||
#include <QCursor>
|
||||
#include <QStyle>
|
||||
#include <QSplitter>
|
||||
|
||||
#ifdef ADS_ANIMATIONS_ENABLED
|
||||
#include <QPropertyAnimation>
|
||||
#include <QParallelAnimationGroup>
|
||||
#endif
|
||||
|
||||
#include "ads/Internal.h"
|
||||
#include "ads/DropOverlay.h"
|
||||
#include "ads/SectionContent.h"
|
||||
#include "ads/SectionWidget.h"
|
||||
#include "ads/FloatingWidget.h"
|
||||
#include "ads/ContainerWidget.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
SectionTitleWidget::SectionTitleWidget(SectionContent::RefPtr content, QWidget* parent) :
|
||||
QFrame(parent),
|
||||
_content(content),
|
||||
_tabMoving(false),
|
||||
_activeTab(false)
|
||||
{
|
||||
QBoxLayout* l = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
l->setSpacing(0);
|
||||
l->addWidget(content->titleWidget());
|
||||
setLayout(l);
|
||||
}
|
||||
|
||||
SectionTitleWidget::~SectionTitleWidget()
|
||||
{
|
||||
layout()->removeWidget(_content->titleWidget());
|
||||
}
|
||||
|
||||
bool SectionTitleWidget::isActiveTab() const
|
||||
{
|
||||
return _activeTab;
|
||||
}
|
||||
|
||||
void SectionTitleWidget::setActiveTab(bool active)
|
||||
{
|
||||
if (active != _activeTab)
|
||||
{
|
||||
_activeTab = active;
|
||||
|
||||
style()->unpolish(this);
|
||||
style()->polish(this);
|
||||
update();
|
||||
|
||||
emit activeTabChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void SectionTitleWidget::mousePressEvent(QMouseEvent* ev)
|
||||
{
|
||||
if (ev->button() == Qt::LeftButton)
|
||||
{
|
||||
ev->accept();
|
||||
_dragStartPos = ev->pos();
|
||||
return;
|
||||
}
|
||||
QFrame::mousePressEvent(ev);
|
||||
}
|
||||
|
||||
void SectionTitleWidget::mouseReleaseEvent(QMouseEvent* ev)
|
||||
{
|
||||
SectionWidget* section = NULL;
|
||||
ContainerWidget* cw = findParentContainerWidget(this);
|
||||
|
||||
// Drop contents of FloatingWidget into SectionWidget.
|
||||
if (_fw)
|
||||
{
|
||||
SectionWidget* sw = cw->sectionAt(cw->mapFromGlobal(ev->globalPos()));
|
||||
if (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 >= QT_VERSION_CHECK(5, 0, 0)
|
||||
_fw.clear();
|
||||
#else
|
||||
_fw = 0;
|
||||
#endif
|
||||
cw->dropContent(data, sw, loc, true);
|
||||
#else
|
||||
QPropertyAnimation* moveAnim = new QPropertyAnimation(_fw, "pos", this);
|
||||
moveAnim->setStartValue(_fw->pos());
|
||||
moveAnim->setEndValue(sw->mapToGlobal(sw->rect().topLeft()));
|
||||
moveAnim->setDuration(ADS_ANIMATION_DURATION);
|
||||
|
||||
QPropertyAnimation* resizeAnim = new QPropertyAnimation(_fw, "size", this);
|
||||
resizeAnim->setStartValue(_fw->size());
|
||||
resizeAnim->setEndValue(sw->size());
|
||||
resizeAnim->setDuration(ADS_ANIMATION_DURATION);
|
||||
|
||||
QParallelAnimationGroup* animGroup = new QParallelAnimationGroup(this);
|
||||
QObject::connect(animGroup, &QPropertyAnimation::finished, [this, data, sw, loc]()
|
||||
{
|
||||
InternalContentData data = _fw->takeContent();
|
||||
_fw->deleteLater();
|
||||
_fw.clear();
|
||||
cw->dropContent(data, sw, loc);
|
||||
});
|
||||
animGroup->addAnimation(moveAnim);
|
||||
animGroup->addAnimation(resizeAnim);
|
||||
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// Mouse is over a outer-edge drop area
|
||||
else
|
||||
{
|
||||
DropArea dropArea = ADS_NS::InvalidDropArea;
|
||||
if (cw->outerTopDropRect().contains(cw->mapFromGlobal(ev->globalPos())))
|
||||
dropArea = ADS_NS::TopDropArea;
|
||||
if (cw->outerRightDropRect().contains(cw->mapFromGlobal(ev->globalPos())))
|
||||
dropArea = ADS_NS::RightDropArea;
|
||||
if (cw->outerBottomDropRect().contains(cw->mapFromGlobal(ev->globalPos())))
|
||||
dropArea = ADS_NS::BottomDropArea;
|
||||
if (cw->outerLeftDropRect().contains(cw->mapFromGlobal(ev->globalPos())))
|
||||
dropArea = ADS_NS::LeftDropArea;
|
||||
|
||||
if (dropArea != ADS_NS::InvalidDropArea)
|
||||
{
|
||||
#if !defined(ADS_ANIMATIONS_ENABLED)
|
||||
InternalContentData data;
|
||||
_fw->takeContent(data);
|
||||
_fw->deleteLater();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
_fw.clear();
|
||||
#else
|
||||
_fw = 0;
|
||||
#endif
|
||||
cw->dropContent(data, NULL, dropArea, true);
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
// End of tab moving, change order now
|
||||
else if (_tabMoving
|
||||
&& (section = findParentSectionWidget(this)) != NULL)
|
||||
{
|
||||
// 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);
|
||||
section->moveContent(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
if (!_dragStartPos.isNull())
|
||||
emit clicked();
|
||||
|
||||
// Reset
|
||||
_dragStartPos = QPoint();
|
||||
_tabMoving = false;
|
||||
cw->_dropOverlay->hideDropOverlay();
|
||||
QFrame::mouseReleaseEvent(ev);
|
||||
}
|
||||
|
||||
void SectionTitleWidget::mouseMoveEvent(QMouseEvent* ev)
|
||||
{
|
||||
ContainerWidget* cw = findParentContainerWidget(this);
|
||||
SectionWidget* section = NULL;
|
||||
|
||||
// Move already existing FloatingWidget
|
||||
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);
|
||||
|
||||
// Show drop indicator
|
||||
if (true)
|
||||
{
|
||||
// Mouse is over a SectionWidget
|
||||
section = cw->sectionAt(cw->mapFromGlobal(QCursor::pos()));
|
||||
if (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())))
|
||||
{
|
||||
cw->_dropOverlay->setAllowedAreas(ADS_NS::TopDropArea);
|
||||
cw->_dropOverlay->showDropOverlay(cw, cw->outerTopDropRect());
|
||||
}
|
||||
else if (cw->outerRightDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
|
||||
{
|
||||
cw->_dropOverlay->setAllowedAreas(ADS_NS::RightDropArea);
|
||||
cw->_dropOverlay->showDropOverlay(cw, cw->outerRightDropRect());
|
||||
}
|
||||
else if (cw->outerBottomDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
|
||||
{
|
||||
cw->_dropOverlay->setAllowedAreas(ADS_NS::BottomDropArea);
|
||||
cw->_dropOverlay->showDropOverlay(cw, cw->outerBottomDropRect());
|
||||
}
|
||||
else if (cw->outerLeftDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
|
||||
{
|
||||
cw->_dropOverlay->setAllowedAreas(ADS_NS::LeftDropArea);
|
||||
cw->_dropOverlay->showDropOverlay(cw, cw->outerLeftDropRect());
|
||||
}
|
||||
else
|
||||
{
|
||||
cw->_dropOverlay->hideDropOverlay();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Begin to drag/float the SectionContent.
|
||||
else if (!_fw && !_dragStartPos.isNull() && (ev->buttons() & Qt::LeftButton)
|
||||
&& (section = findParentSectionWidget(this)) != NULL
|
||||
&& !section->titleAreaGeometry().contains(section->mapFromGlobal(ev->globalPos())))
|
||||
{
|
||||
ev->accept();
|
||||
|
||||
// Create floating widget.
|
||||
InternalContentData data;
|
||||
if (!section->takeContent(_content->uid(), data))
|
||||
{
|
||||
qWarning() << "THIS SHOULD NOT HAPPEN!!" << _content->uid() << _content->uniqueName();
|
||||
return;
|
||||
}
|
||||
|
||||
_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...
|
||||
|
||||
const QPoint moveToPos = ev->globalPos() - (_dragStartPos + QPoint(ADS_WINDOW_FRAME_BORDER_WIDTH, ADS_WINDOW_FRAME_BORDER_WIDTH));
|
||||
_fw->move(moveToPos);
|
||||
_fw->show();
|
||||
|
||||
// Delete old section, if it is empty now.
|
||||
if (section->contents().isEmpty())
|
||||
{
|
||||
delete section;
|
||||
section = NULL;
|
||||
}
|
||||
deleteEmptySplitter(cw);
|
||||
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);
|
||||
|
||||
return;
|
||||
}
|
||||
// Begin to drag title inside the title area to switch its position inside the SectionWidget.
|
||||
else if (!_dragStartPos.isNull() && (ev->buttons() & Qt::LeftButton)
|
||||
&& (ev->pos() - _dragStartPos).manhattanLength() >= QApplication::startDragDistance() // Wait a few pixels before start moving
|
||||
&& (section = findParentSectionWidget(this)) != NULL
|
||||
&& section->titleAreaGeometry().contains(section->mapFromGlobal(ev->globalPos())))
|
||||
{
|
||||
ev->accept();
|
||||
|
||||
_tabMoving = true;
|
||||
raise(); // Raise current title-widget above other tabs
|
||||
|
||||
return;
|
||||
}
|
||||
QFrame::mouseMoveEvent(ev);
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
@ -0,0 +1,458 @@
|
||||
#include "ads/SectionWidget.h"
|
||||
|
||||
#include <QApplication>
|
||||
#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>
|
||||
#endif
|
||||
|
||||
#include "ads/Internal.h"
|
||||
#include "ads/DropOverlay.h"
|
||||
#include "ads/SectionContent.h"
|
||||
#include "ads/SectionTitleWidget.h"
|
||||
#include "ads/SectionContentWidget.h"
|
||||
#include "ads/FloatingWidget.h"
|
||||
#include "ads/ContainerWidget.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
SectionWidget::SectionWidget(ContainerWidget* parent) :
|
||||
QFrame(parent),
|
||||
_uid(GetNextUid()),
|
||||
_container(parent),
|
||||
_tabsLayout(NULL),
|
||||
_tabsLayoutInitCount(0),
|
||||
_contentsLayout(NULL),
|
||||
_mousePressTitleWidget(NULL)
|
||||
{
|
||||
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
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);
|
||||
_tabsContainerWidget->setLayout(_tabsLayout);
|
||||
|
||||
_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(_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);
|
||||
l->addLayout(_contentsLayout, 1);
|
||||
|
||||
#if defined(ADS_ANIMATIONS_ENABLED)
|
||||
QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect(this);
|
||||
shadow->setOffset(0, 0);
|
||||
shadow->setBlurRadius(8);
|
||||
setGraphicsEffect(shadow);
|
||||
#endif
|
||||
|
||||
SWLookupMapById(_container).insert(_uid, this);
|
||||
}
|
||||
|
||||
SectionWidget::~SectionWidget()
|
||||
{
|
||||
if (_container)
|
||||
{
|
||||
SWLookupMapById(_container).remove(_uid);
|
||||
_container->_sections.removeAll(this); // Note: I don't like this here, but we have to remove it from list...
|
||||
}
|
||||
|
||||
// Delete empty QSplitter.
|
||||
QSplitter* splitter = findParentSplitter(this);
|
||||
if (splitter && splitter->count() == 0)
|
||||
{
|
||||
splitter->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
int SectionWidget::uid() const
|
||||
{
|
||||
return _uid;
|
||||
}
|
||||
|
||||
ContainerWidget* SectionWidget::containerWidget() const
|
||||
{
|
||||
return _container;
|
||||
}
|
||||
|
||||
QRect SectionWidget::titleAreaGeometry() const
|
||||
{
|
||||
return _topLayout->geometry();
|
||||
}
|
||||
|
||||
QRect SectionWidget::contentAreaGeometry() const
|
||||
{
|
||||
return _contentsLayout->geometry();
|
||||
}
|
||||
|
||||
void SectionWidget::addContent(const SectionContent::RefPtr& c)
|
||||
{
|
||||
_contents.append(c);
|
||||
|
||||
SectionTitleWidget* title = new SectionTitleWidget(c, NULL);
|
||||
_sectionTitles.append(title);
|
||||
_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()));
|
||||
#endif
|
||||
|
||||
SectionContentWidget* content = new SectionContentWidget(c, NULL);
|
||||
_sectionContents.append(content);
|
||||
_contentsLayout->addWidget(content);
|
||||
|
||||
// Active first TAB.
|
||||
if (_contents.size() == 1)
|
||||
setCurrentIndex(0);
|
||||
// Switch to newest.
|
||||
// else
|
||||
// setCurrentIndex(_contentsLayout->count() - 1);
|
||||
|
||||
updateTabsMenu();
|
||||
}
|
||||
|
||||
void SectionWidget::addContent(const InternalContentData& data, bool autoActivate)
|
||||
{
|
||||
_contents.append(data.content);
|
||||
|
||||
// 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() - _tabsLayoutInitCount, data.titleWidget);
|
||||
data.titleWidget->setVisible(true);
|
||||
#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()));
|
||||
#endif
|
||||
|
||||
// Add content-widget to stack.
|
||||
// Visibility is managed by QStackedWidget.
|
||||
_sectionContents.append(data.contentWidget);
|
||||
_contentsLayout->addWidget(data.contentWidget);
|
||||
|
||||
// Activate first TAB.
|
||||
if (_contents.size() == 1)
|
||||
setCurrentIndex(0);
|
||||
// Switch to just added TAB.
|
||||
else if (autoActivate)
|
||||
setCurrentIndex(_contents.count() - 1);
|
||||
// Mark it as inactive tab.
|
||||
else
|
||||
data.titleWidget->setActiveTab(false); // or: setCurrentIndex(currentIndex())
|
||||
|
||||
updateTabsMenu();
|
||||
}
|
||||
|
||||
bool SectionWidget::takeContent(int uid, InternalContentData& data)
|
||||
{
|
||||
// Find SectionContent.
|
||||
SectionContent::RefPtr sc;
|
||||
int index = -1;
|
||||
for (int i = 0; i < _contents.count(); i++)
|
||||
{
|
||||
if (_contents[i]->uid() != uid)
|
||||
continue;
|
||||
index = i;
|
||||
sc = _contents.takeAt(i);
|
||||
break;
|
||||
}
|
||||
if (!sc)
|
||||
return false;
|
||||
|
||||
// Title wrapper widget (TAB)
|
||||
SectionTitleWidget* title = _sectionTitles.takeAt(index);
|
||||
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)
|
||||
SectionContentWidget* content = _sectionContents.takeAt(index);
|
||||
if (content)
|
||||
{
|
||||
_contentsLayout->removeWidget(content);
|
||||
content->disconnect(this);
|
||||
content->setParent(_container);
|
||||
}
|
||||
|
||||
// Select the previous tab as activeTab.
|
||||
if (_contents.size() > 0 && title->isActiveTab())
|
||||
{
|
||||
if (index > 0)
|
||||
setCurrentIndex(index - 1);
|
||||
else
|
||||
setCurrentIndex(0);
|
||||
}
|
||||
|
||||
updateTabsMenu();
|
||||
|
||||
data.content = sc;
|
||||
data.titleWidget = title;
|
||||
data.contentWidget = content;
|
||||
return !data.content.isNull();
|
||||
}
|
||||
|
||||
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;
|
||||
for (int i = 0; i < _sectionTitles.size(); ++i)
|
||||
{
|
||||
if (_sectionTitles[i]->geometry().contains(p) && (exclude == NULL || _sectionTitles[i] != exclude))
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
int SectionWidget::currentIndex() const
|
||||
{
|
||||
return _contentsLayout->currentIndex();
|
||||
}
|
||||
|
||||
void SectionWidget::moveContent(int from, int to)
|
||||
{
|
||||
if (from >= _contents.size() || from < 0 || to >= _contents.size() || to < 0 || from == to)
|
||||
{
|
||||
qDebug() << "Invalid index for tab movement" << from << to;
|
||||
_tabsLayout->update();
|
||||
return;
|
||||
}
|
||||
|
||||
_contents.move(from, to);
|
||||
_sectionTitles.move(from, to);
|
||||
_sectionContents.move(from, to);
|
||||
|
||||
QLayoutItem* liFrom = NULL;
|
||||
liFrom = _tabsLayout->takeAt(from);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
_tabsLayout->insertItem(to, liFrom);
|
||||
#else
|
||||
_tabsLayout->insertWidget(to, liFrom->widget());
|
||||
delete liFrom;
|
||||
liFrom = NULL;
|
||||
#endif
|
||||
|
||||
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)
|
||||
{
|
||||
if (index < 0 || index > _contents.count() - 1)
|
||||
{
|
||||
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set active TAB
|
||||
for (int i = 0; i < _tabsLayout->count(); ++i)
|
||||
{
|
||||
QLayoutItem* item = _tabsLayout->itemAt(i);
|
||||
if (item->widget())
|
||||
{
|
||||
SectionTitleWidget* stw = dynamic_cast<SectionTitleWidget*>(item->widget());
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set active CONTENT
|
||||
_contentsLayout->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void SectionWidget::onSectionTitleClicked()
|
||||
{
|
||||
SectionTitleWidget* stw = qobject_cast<SectionTitleWidget*>(sender());
|
||||
if (stw)
|
||||
{
|
||||
int index = _tabsLayout->indexOf(stw);
|
||||
setCurrentIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
void SectionWidget::onCloseButtonClicked()
|
||||
{
|
||||
const int index = currentIndex();
|
||||
if (index < 0 || index > _contents.size() - 1)
|
||||
return;
|
||||
SectionContent::RefPtr sc = _contents.at(index);
|
||||
if (sc.isNull())
|
||||
return;
|
||||
_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;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
@ -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
|
@ -0,0 +1,55 @@
|
||||
TARGET = AdvancedDockingSystemDemo
|
||||
|
||||
QT += core gui
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
greaterThan(QT_MAJOR_VERSION, 4): DEFINES += ADS_NAMESPACE_ENABLED
|
||||
|
||||
windows {
|
||||
# MinGW
|
||||
*-g++* {
|
||||
QMAKE_CXXFLAGS += -std=c++11
|
||||
}
|
||||
# MSVC
|
||||
*-msvc* {
|
||||
}
|
||||
}
|
||||
|
||||
SOURCES += \
|
||||
src/main.cpp \
|
||||
src/mainwindow.cpp \
|
||||
src/icontitlewidget.cpp \
|
||||
src/dialogs/SectionContentListModel.cpp \
|
||||
src/dialogs/SectionContentListWidget.cpp
|
||||
|
||||
HEADERS += \
|
||||
src/mainwindow.h \
|
||||
src/icontitlewidget.h \
|
||||
src/dialogs/SectionContentListModel.h \
|
||||
src/dialogs/SectionContentListWidget.h
|
||||
|
||||
FORMS += \
|
||||
src/mainwindow.ui \
|
||||
src/dialogs/SectionContentListWidget.ui
|
||||
|
||||
|
||||
# Dependency: AdvancedDockingSystem (staticlib)
|
||||
#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
|
||||
|
||||
#INCLUDEPATH += $$PWD/../AdvancedDockingSystem/include
|
||||
#DEPENDPATH += $$PWD/../AdvancedDockingSystem/include
|
||||
|
||||
#win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/release/libAdvancedDockingSystem.a
|
||||
#else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/debug/libAdvancedDockingSystem.a
|
||||
#else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/release/AdvancedDockingSystem.lib
|
||||
#else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/debug/AdvancedDockingSystem.lib
|
||||
#else:unix: PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/libAdvancedDockingSystem.a
|
||||
|
||||
# Dependency: AdvancedDockingSystem (shared)
|
||||
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
|
@ -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;
|
||||
}
|
@ -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
|
@ -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());
|
||||
}
|
@ -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
|
@ -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>
|
@ -0,0 +1,65 @@
|
||||
#include "icontitlewidget.h"
|
||||
|
||||
#include <QIcon>
|
||||
#include <QString>
|
||||
#include <QBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QStyle>
|
||||
|
||||
IconTitleWidget::IconTitleWidget(const QIcon& icon, const QString& title, QWidget *parent) :
|
||||
QFrame(parent)
|
||||
{
|
||||
QBoxLayout* l = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(l);
|
||||
|
||||
_iconLabel = new QLabel();
|
||||
l->addWidget(_iconLabel);
|
||||
|
||||
_titleLabel = new QLabel();
|
||||
l->addWidget(_titleLabel, 1);
|
||||
|
||||
setIcon(icon);
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
void IconTitleWidget::setIcon(const QIcon& icon)
|
||||
{
|
||||
if (icon.isNull())
|
||||
{
|
||||
_iconLabel->setPixmap(QPixmap());
|
||||
_iconLabel->setVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_iconLabel->setPixmap(icon.pixmap(16, 16));
|
||||
_iconLabel->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
void IconTitleWidget::setTitle(const QString& title)
|
||||
{
|
||||
if (title.isEmpty())
|
||||
{
|
||||
_titleLabel->setText(QString());
|
||||
_titleLabel->setVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_titleLabel->setText(title);
|
||||
_titleLabel->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
void IconTitleWidget::polishUpdate()
|
||||
{
|
||||
QList<QWidget*> widgets;
|
||||
widgets.append(_iconLabel);
|
||||
widgets.append(_titleLabel);
|
||||
foreach (QWidget* w, widgets)
|
||||
{
|
||||
w->style()->unpolish(w);
|
||||
w->style()->polish(w);
|
||||
w->update();
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
#ifndef ICONTITLEWIDGET_H
|
||||
#define ICONTITLEWIDGET_H
|
||||
|
||||
#include <QFrame>
|
||||
class QIcon;
|
||||
class QString;
|
||||
class QLabel;
|
||||
|
||||
class IconTitleWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit IconTitleWidget(const QIcon& icon, const QString& title, QWidget *parent = 0);
|
||||
|
||||
public slots:
|
||||
void setIcon(const QIcon& icon);
|
||||
void setTitle(const QString& title);
|
||||
void polishUpdate();
|
||||
|
||||
public:
|
||||
QLabel* _iconLabel;
|
||||
QLabel* _titleLabel;
|
||||
};
|
||||
|
||||
#endif // ICONTITLEWIDGET_H
|
@ -0,0 +1,28 @@
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <QApplication>
|
||||
|
||||
#include "mainwindow.h"
|
||||
|
||||
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();
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include <QTime>
|
||||
#include <QLabel>
|
||||
#include <QTextEdit>
|
||||
#include <QCalendarWidget>
|
||||
#include <QFrame>
|
||||
#include <QTreeView>
|
||||
#include <QFileSystemModel>
|
||||
#include <QBoxLayout>
|
||||
|
||||
#include "ads/SectionWidget.h"
|
||||
#include "ads/DropOverlay.h"
|
||||
|
||||
#include "dialogs/SectionContentListWidget.h"
|
||||
|
||||
#include "icontitlewidget.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
static int CONTENT_COUNT = 0;
|
||||
|
||||
static ADS_NS::SectionContent::RefPtr createLongTextLabelSC(ADS_NS::ContainerWidget* container)
|
||||
{
|
||||
QWidget* w = new QWidget();
|
||||
QBoxLayout* bl = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
w->setLayout(bl);
|
||||
|
||||
QLabel* l = new QLabel();
|
||||
l->setWordWrap(true);
|
||||
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||
l->setText(QString("Lorem Ipsum ist ein einfacher Demo-Text für die Print- und Schriftindustrie. Lorem Ipsum ist in der Industrie bereits der Standard Demo-Text seit 1500, als ein unbekannter Schriftsteller eine Hand voll Wörter nahm und diese durcheinander warf um ein Musterbuch zu erstellen. Es hat nicht nur 5 Jahrhunderte überlebt, sondern auch in Spruch in die elektronische Schriftbearbeitung geschafft (bemerke, nahezu unverändert). Bekannt wurde es 1960, mit dem erscheinen von Letrase, welches Passagen von Lorem Ipsum enhielt, so wie Desktop Software wie Aldus PageMaker - ebenfalls mit Lorem Ipsum."));
|
||||
bl->addWidget(l);
|
||||
|
||||
const int index = ++CONTENT_COUNT;
|
||||
ADS_NS::SectionContent::RefPtr sc = ADS_NS::SectionContent::newSectionContent(QString("uname-%1").arg(index), container, new IconTitleWidget(QIcon(), QString("Label %1").arg(index)), w);
|
||||
sc->setTitle("Ein Label " + QString::number(index));
|
||||
return sc;
|
||||
}
|
||||
|
||||
static ADS_NS::SectionContent::RefPtr createCalendarSC(ADS_NS::ContainerWidget* container)
|
||||
{
|
||||
QCalendarWidget* w = new QCalendarWidget();
|
||||
|
||||
const int index = ++CONTENT_COUNT;
|
||||
return ADS_NS::SectionContent::newSectionContent(QString("uname-%1").arg(index), container, new IconTitleWidget(QIcon(), QString("Calendar %1").arg(index)), w);
|
||||
}
|
||||
|
||||
static ADS_NS::SectionContent::RefPtr createFileSystemTreeSC(ADS_NS::ContainerWidget* container)
|
||||
{
|
||||
QTreeView* w = new QTreeView();
|
||||
w->setFrameShape(QFrame::NoFrame);
|
||||
// QFileSystemModel* m = new QFileSystemModel(w);
|
||||
// m->setRootPath(QDir::currentPath());
|
||||
// w->setModel(m);
|
||||
|
||||
const int index = ++CONTENT_COUNT;
|
||||
return ADS_NS::SectionContent::newSectionContent(QString("uname-%1").arg(index), container, new IconTitleWidget(QIcon(), QString("Filesystem %1").arg(index)), w);
|
||||
}
|
||||
|
||||
static void storeDataHelper(const QString& fname, const QByteArray& ba)
|
||||
{
|
||||
QFile f(fname + QString(".dat"));
|
||||
if (f.open(QFile::WriteOnly))
|
||||
{
|
||||
f.write(ba);
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
static QByteArray loadDataHelper(const QString& fname)
|
||||
{
|
||||
QFile f(fname + QString(".dat"));
|
||||
if (f.open(QFile::ReadOnly))
|
||||
{
|
||||
QByteArray ba = f.readAll();
|
||||
f.close();
|
||||
return ba;
|
||||
}
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// Setup actions.
|
||||
QObject::connect(ui->actionContentList, SIGNAL(triggered()), this, SLOT(showSectionContentListDialog()));
|
||||
|
||||
// ADS - Create main container (ContainerWidget).
|
||||
_container = new ADS_NS::ContainerWidget();
|
||||
#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.
|
||||
if (true)
|
||||
{
|
||||
// Test #1: Use high-level public API
|
||||
ADS_NS::ContainerWidget* cw = _container;
|
||||
ADS_NS::SectionWidget* sw = NULL;
|
||||
|
||||
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::CenterDropArea);
|
||||
sw = _container->addSectionContent(createCalendarSC(cw), sw, ADS_NS::RightDropArea);
|
||||
sw = _container->addSectionContent(createFileSystemTreeSC(cw), sw, ADS_NS::CenterDropArea);
|
||||
|
||||
_container->addSectionContent(createCalendarSC(_container));
|
||||
_container->addSectionContent(createLongTextLabelSC(_container));
|
||||
_container->addSectionContent(createLongTextLabelSC(_container));
|
||||
_container->addSectionContent(createLongTextLabelSC(_container));
|
||||
|
||||
ADS_NS::SectionContent::RefPtr sc = createLongTextLabelSC(cw);
|
||||
sc->setFlags(static_cast<ADS_NS::SectionContent::Flag>(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
|
||||
resize(800, 600);
|
||||
restoreGeometry(loadDataHelper("MainWindow"));
|
||||
|
||||
// ADS - Restore geometries and states of contents.
|
||||
_container->restoreState(loadDataHelper("ContainerWidget"));
|
||||
}
|
||||
|
||||
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);
|
||||
IconTitleWidget* itw = dynamic_cast<IconTitleWidget*>(sc->titleWidget());
|
||||
if (itw)
|
||||
{
|
||||
itw->polishUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onSectionContentVisibilityChanged(const ADS_NS::SectionContent::RefPtr& sc, bool visible)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << sc->uniqueName() << visible;
|
||||
}
|
||||
|
||||
void MainWindow::onActionAddSectionContentTriggered()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void MainWindow::contextMenuEvent(QContextMenuEvent* e)
|
||||
{
|
||||
Q_UNUSED(e);
|
||||
QMenu* m = _container->createContextMenu();
|
||||
m->exec(QCursor::pos());
|
||||
delete m;
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent* e)
|
||||
{
|
||||
Q_UNUSED(e);
|
||||
storeDataHelper("MainWindow", saveGeometry());
|
||||
storeDataHelper("ContainerWidget", _container->saveState());
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include "ads/API.h"
|
||||
#include "ads/ContainerWidget.h"
|
||||
#include "ads/SectionContent.h"
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
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();
|
||||
|
||||
protected:
|
||||
virtual void contextMenuEvent(QContextMenuEvent* e);
|
||||
virtual void closeEvent(QCloseEvent* e);
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
ADS_NS::ContainerWidget* _container;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget"/>
|
||||
<widget class="QStatusBar" name="statusBar"/>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<addaction name="actionExit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>View</string>
|
||||
</property>
|
||||
<addaction name="actionContentList"/>
|
||||
<addaction name="actionDemo_2"/>
|
||||
<addaction name="actionDemo_3"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuAbout">
|
||||
<property name="title">
|
||||
<string>About</string>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuView"/>
|
||||
<addaction name="menuAbout"/>
|
||||
</widget>
|
||||
<action name="actionAddSectionContent">
|
||||
<property name="text">
|
||||
<string>Add SectionContent</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionContentList">
|
||||
<property name="text">
|
||||
<string>Contents...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDemo_2">
|
||||
<property name="text">
|
||||
<string>Demo 2</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDemo_3">
|
||||
<property name="text">
|
||||
<string>Demo 3</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExit">
|
||||
<property name="text">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,7 @@
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/src/TestCore.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/src/main.cpp \
|
||||
$$PWD/src/TestCore.cpp
|
@ -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)
|
@ -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)
|
@ -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
|
@ -0,0 +1 @@
|
||||
#include <QtTest/QtTest>
|
94
import/Qt-Advanced-Docking-System-1.0.0/README.md
Normal file
@ -0,0 +1,94 @@
|
||||
# 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.
|
||||
|
||||
![Layout of widgets](preview.png)
|
||||
![Dropping widgets](preview-dragndrop.png)
|
||||
|
||||
## Tested Compatible Environments
|
||||
- Windows 7 / 8 / 8.1 / 10
|
||||
- Ubuntu 15.10
|
||||
|
||||
## Build
|
||||
Open the `build.pro` with QtCreator and start the build, that's it.
|
||||
You can run the demo project and test it yourself.
|
||||
|
||||
## Release & Development
|
||||
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
|
||||
|
||||
## License information
|
||||
![WTFPL](license.png)
|
||||
|
||||
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).
|
6
import/Qt-Advanced-Docking-System-1.0.0/build.pro
Normal file
@ -0,0 +1,6 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
AdvancedDockingSystem \
|
||||
AdvancedDockingSystemDemo \
|
||||
AdvancedDockingSystemUnitTests
|
BIN
import/Qt-Advanced-Docking-System-1.0.0/license.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
import/Qt-Advanced-Docking-System-1.0.0/preview-dragndrop.png
Normal file
After Width: | Height: | Size: 191 KiB |
BIN
import/Qt-Advanced-Docking-System-1.0.0/preview.png
Normal file
After Width: | Height: | Size: 53 KiB |
16
main.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include <QApplication>
|
||||
#include "ui/mainwindow.h"
|
||||
#include <properties/config.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
a.setApplicationName(APP_NAME);
|
||||
a.setApplicationDisplayName(APP_NAME);
|
||||
a.setApplicationVersion(APP_VERSION);
|
||||
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
return a.exec();
|
||||
}
|
4
main.py
@ -1,4 +0,0 @@
|
||||
import sys
|
||||
from ui.ember_application import EmberApplication
|
||||
|
||||
EmberApplication(sys.argv).start()
|
@ -1,2 +0,0 @@
|
||||
from .recent_project import RecentProject
|
||||
from .project import Project, ProjectItem, ItemType
|
@ -1,2 +0,0 @@
|
||||
class CompositionClip(object):
|
||||
pass
|
95
model/project.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#include "project.h"
|
||||
|
||||
namespace Ember
|
||||
{
|
||||
|
||||
Project::Project(boost::filesystem::path projectFilePath)
|
||||
: m_path(projectFilePath),
|
||||
m_name(),
|
||||
m_bitsPerChannel(8),
|
||||
m_audioSampleRate(48000),
|
||||
m_rootItem(projectFilePath.parent_path(), nullptr, this)
|
||||
{
|
||||
}
|
||||
|
||||
boost::filesystem::path Project::path() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
std::string Project::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
int Project::bitsPerChannel() const
|
||||
{
|
||||
return m_bitsPerChannel;
|
||||
}
|
||||
|
||||
int Project::audioSampleRate() const
|
||||
{
|
||||
return m_audioSampleRate;
|
||||
}
|
||||
|
||||
ProjectItem& Project::rootItem()
|
||||
{
|
||||
return m_rootItem;
|
||||
}
|
||||
|
||||
void Project::setName(const std::string &name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
void Project::setBitsPerChannel(int bitsPerChannel)
|
||||
{
|
||||
m_bitsPerChannel = bitsPerChannel;
|
||||
}
|
||||
|
||||
void Project::setAudioSampleRate(int audioSampleRate)
|
||||
{
|
||||
m_audioSampleRate = audioSampleRate;
|
||||
}
|
||||
|
||||
void Project::load()
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
|
||||
if (!doc.load_file(m_path.c_str()))
|
||||
{
|
||||
// TODO: proper error handlign
|
||||
throw 0;
|
||||
}
|
||||
|
||||
pugi::xml_node root = doc.document_element();
|
||||
|
||||
// Get name
|
||||
pugi::xml_attribute attr;
|
||||
if ((attr = root.attribute("name")))
|
||||
m_name = attr.as_string();
|
||||
|
||||
// Get other properties
|
||||
if ((attr = root.attribute("bitsPerChannel")))
|
||||
m_bitsPerChannel = attr.as_int();
|
||||
|
||||
if ((attr = root.attribute("audioSampleRate")))
|
||||
m_audioSampleRate = attr.as_int();
|
||||
}
|
||||
|
||||
void Project::save()
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
doc.append_child(pugi::node_declaration);
|
||||
|
||||
auto proj = doc.append_child("project");
|
||||
proj.append_attribute("name").set_value(m_name.c_str());
|
||||
proj.append_attribute("bitsPerChannel").set_value(m_bitsPerChannel);
|
||||
proj.append_attribute("audioSampleRate").set_value(m_audioSampleRate);
|
||||
|
||||
doc.save_file(m_path.string().c_str());
|
||||
}
|
||||
|
||||
}
|
57
model/project.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef PROJECT_H
|
||||
#define PROJECT_H
|
||||
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "projectitem.h"
|
||||
|
||||
namespace Ember
|
||||
{
|
||||
|
||||
class Project
|
||||
{
|
||||
public:
|
||||
Project(boost::filesystem::path projectFilePath);
|
||||
|
||||
// Getters
|
||||
boost::filesystem::path path() const;
|
||||
|
||||
std::string name() const;
|
||||
int bitsPerChannel() const;
|
||||
int audioSampleRate() const;
|
||||
|
||||
ProjectItem& rootItem();
|
||||
|
||||
// Setters
|
||||
void setName(const std::string &name);
|
||||
void setBitsPerChannel(int bitsPerChannel);
|
||||
void setAudioSampleRate(int audioSampleRate);
|
||||
|
||||
// Actions
|
||||
void load();
|
||||
void save();
|
||||
|
||||
private:
|
||||
boost::filesystem::path m_path;
|
||||
|
||||
// Details (that will be stored in project file)
|
||||
std::string m_name;
|
||||
int m_bitsPerChannel = 8;
|
||||
int m_audioSampleRate = 48000;
|
||||
|
||||
// Items
|
||||
ProjectItem m_rootItem;
|
||||
};
|
||||
|
||||
struct RecentProject
|
||||
{
|
||||
std::string name;
|
||||
boost::filesystem::path path;
|
||||
time_t access;
|
||||
bool pinned;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // PROJECT_H
|
@ -1,74 +0,0 @@
|
||||
import os
|
||||
from enum import Enum
|
||||
from typing import List
|
||||
|
||||
class ItemType(Enum):
|
||||
MISSING = 0
|
||||
DIRECTORY = 1
|
||||
IMAGE = 2
|
||||
AUDIO = 3
|
||||
VIDEO = 4
|
||||
SUBTITLES = 5
|
||||
PROJECT = 6
|
||||
COMPOSITION = 7
|
||||
SEQUENCE = 8
|
||||
UNKNOWN = 1000
|
||||
|
||||
class ProjectItem(object):
|
||||
def __init__(self, name : str, project : "Project", storage : "ProjectStorage", parent : "ProjectItem" = None):
|
||||
self.__storage = storage
|
||||
self.name = name
|
||||
self.project = project
|
||||
self.parent = parent
|
||||
self.__type : ItemType = None
|
||||
self.__children : list = None
|
||||
|
||||
"""
|
||||
Gets the path relative to the project directory.
|
||||
|
||||
Returns:
|
||||
path relative to the project root folder.
|
||||
"""
|
||||
def path(self) -> str:
|
||||
if self.parent:
|
||||
return os.path.join(self.parent.path(), self.name)
|
||||
return self.name
|
||||
|
||||
"""
|
||||
Gets the absolute path to this project item.
|
||||
"""
|
||||
def absolutePath(self) -> str:
|
||||
return os.path.join(self.project.rootDir, self.path())
|
||||
|
||||
"""
|
||||
Gets the type of this project item.
|
||||
"""
|
||||
def itemType(self) -> str:
|
||||
if self.__type is None:
|
||||
self.__type = self.__storage.itemType(self)
|
||||
return self.__type
|
||||
|
||||
"""
|
||||
Gets the children project items.
|
||||
"""
|
||||
def children(self) -> List["ProjectItem"]:
|
||||
if self.__children is None:
|
||||
self.__children = list(self.__storage.itemChildren(self))
|
||||
return self.__children
|
||||
|
||||
|
||||
class Project(object):
|
||||
|
||||
def __init__(self, path : str, storage : "ProjectStorage"):
|
||||
self.__storage = storage
|
||||
self.projectFile : str = path
|
||||
self.rootDir : str = None
|
||||
self.videoBitsPerChannel : int = 8
|
||||
self.videoColorSpace : str = None
|
||||
self.audioSampleRate : int = 48000
|
||||
self.__items : List[ProjectItem] = None
|
||||
|
||||
def items(self) -> List[ProjectItem]:
|
||||
if self.__items is None:
|
||||
self.__items = list(self.__storage.projectItems(self))
|
||||
return self.__items
|
137
model/projectitem.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
#include <algorithm>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <properties/config.h>
|
||||
|
||||
#include "project.h"
|
||||
#include "projectitem.h"
|
||||
|
||||
namespace Ember
|
||||
{
|
||||
|
||||
ProjectItem::ProjectItem(boost::filesystem::path path, ProjectItem* parent, Project* project)
|
||||
: m_path(path),
|
||||
m_type(),
|
||||
m_typeLoaded(false),
|
||||
m_parent(parent),
|
||||
m_children(),
|
||||
m_childrenLoaded(false),
|
||||
m_project(project)
|
||||
{
|
||||
}
|
||||
|
||||
std::string ProjectItem::name() const
|
||||
{
|
||||
return m_path.filename().string();
|
||||
}
|
||||
|
||||
ProjectItem::ProjectItemType ProjectItem::type()
|
||||
{
|
||||
if (!m_typeLoaded)
|
||||
{
|
||||
if (boost::filesystem::is_directory(m_path))
|
||||
{
|
||||
m_type = ProjectItemType::DIRECTORY;
|
||||
}
|
||||
else if (boost::filesystem::exists(m_path))
|
||||
{
|
||||
std::string ext = m_path.extension().string();
|
||||
|
||||
if (ext == ".png" || ext == ".jpg" || ext == ".bmp" || ext == ".jpeg")
|
||||
m_type = ProjectItemType::IMAGE;
|
||||
else if (ext == ".mp3" || ext == ".ogg" || ext == ".wav")
|
||||
m_type = ProjectItemType::AUDIO;
|
||||
else if (ext == ".avi" || ext == ".mp4" || ext == ".wmv")
|
||||
m_type = ProjectItemType::VIDEO;
|
||||
else if (ext == ".srt")
|
||||
m_type = ProjectItemType::SUBTITLES;
|
||||
else if (ext == EMBER_PROJECT_EXTENSION)
|
||||
m_type = ProjectItemType::PROJECT;
|
||||
else if (ext == EMBER_COMPOSITION_EXTENSION)
|
||||
m_type = ProjectItemType::COMPOSITION;
|
||||
else if (ext == EMBER_SEQUENCE_EXTENSION)
|
||||
m_type = ProjectItemType::SEQUENCE;
|
||||
|
||||
else m_type = ProjectItemType::UNKNOWN;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_type = ProjectItemType::MISSING;
|
||||
}
|
||||
|
||||
m_typeLoaded = true;
|
||||
}
|
||||
|
||||
return m_type;
|
||||
}
|
||||
|
||||
ProjectItem *ProjectItem::parent() const
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
Project *ProjectItem::project() const
|
||||
{
|
||||
return m_project;
|
||||
}
|
||||
|
||||
const std::vector<ProjectItem *>& ProjectItem::children()
|
||||
{
|
||||
if (!m_childrenLoaded)
|
||||
{
|
||||
if (type() == ProjectItemType::DIRECTORY)
|
||||
{
|
||||
for (auto&& entry : boost::filesystem::directory_iterator(m_path))
|
||||
{
|
||||
ProjectItem* child = new ProjectItem(entry.path(), this, m_project);
|
||||
m_children.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
m_childrenLoaded = true;
|
||||
}
|
||||
|
||||
return m_children;
|
||||
}
|
||||
|
||||
boost::filesystem::path ProjectItem::path() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
void ProjectItem::rename(const std::string &name)
|
||||
{
|
||||
auto newPath = m_path.parent_path().append(name);
|
||||
boost::filesystem::rename(m_path, newPath);
|
||||
}
|
||||
|
||||
void ProjectItem::move(ProjectItem *newParent)
|
||||
{
|
||||
auto newPath = newParent->path().append(name());
|
||||
boost::filesystem::rename(m_path, newPath);
|
||||
|
||||
// Update parenting
|
||||
if (m_parent)
|
||||
m_parent->unparentChild(this);
|
||||
|
||||
newParent->m_children.push_back(this);
|
||||
}
|
||||
|
||||
void ProjectItem::unlink()
|
||||
{
|
||||
boost::filesystem::remove(m_path);
|
||||
|
||||
// Update parenting
|
||||
if (m_parent)
|
||||
delete m_parent->unparentChild(this);
|
||||
}
|
||||
|
||||
ProjectItem *ProjectItem::unparentChild(ProjectItem *child)
|
||||
{
|
||||
auto it = std::find(m_children.begin(), m_children.end(), child);
|
||||
if (it != m_children.end())
|
||||
m_parent->m_children.erase(it);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
}
|
105
model/projectitem.h
Normal file
@ -0,0 +1,105 @@
|
||||
#ifndef PROJECTITEM_H
|
||||
#define PROJECTITEM_H
|
||||
|
||||
#include <string>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace Ember
|
||||
{
|
||||
class Project;
|
||||
|
||||
class ProjectItem
|
||||
{
|
||||
public:
|
||||
enum ProjectItemType
|
||||
{
|
||||
UNKNOWN,
|
||||
MISSING,
|
||||
DIRECTORY,
|
||||
IMAGE,
|
||||
AUDIO,
|
||||
VIDEO,
|
||||
SUBTITLES,
|
||||
PROJECT,
|
||||
COMPOSITION,
|
||||
SEQUENCE
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief ProjectItem constructor
|
||||
* @param name Name corresponding to file on disk
|
||||
* @param parent Parent project item
|
||||
* @param project Assigned project
|
||||
*/
|
||||
ProjectItem(boost::filesystem::path path, ProjectItem* parent, Project* project);
|
||||
|
||||
// Getters
|
||||
/**
|
||||
* @brief Gets the name of the item
|
||||
* @return Name
|
||||
*/
|
||||
std::string name() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the type of the item.
|
||||
* @return Type of item
|
||||
*/
|
||||
ProjectItemType type();
|
||||
|
||||
/**
|
||||
* @brief Gets the parent project item.
|
||||
* @return Parent item
|
||||
*/
|
||||
ProjectItem *parent() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the parent project
|
||||
* @return Parent project
|
||||
*/
|
||||
Project *project() const;
|
||||
|
||||
/**
|
||||
* @brief Gets a list of children. If not a directory, the list will be empty.
|
||||
* @return Vector of children.
|
||||
*/
|
||||
const std::vector<ProjectItem *>& children();
|
||||
|
||||
/**
|
||||
* @brief Gets the path of this project item
|
||||
* @return
|
||||
*/
|
||||
boost::filesystem::path path() const;
|
||||
|
||||
// Action
|
||||
/**
|
||||
* @brief Sets the name of the item. Will result in a disk rename.
|
||||
* @param name New name
|
||||
*/
|
||||
void rename(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Sets the parent project item. Will result in a disk move.
|
||||
* @param newParent New parent
|
||||
*/
|
||||
void move(ProjectItem *newParent);
|
||||
|
||||
/**
|
||||
* @brief Delete from the disk. 'this' object is DELETED!
|
||||
* TODO: recycle bin or permanent delete?
|
||||
*/
|
||||
void unlink();
|
||||
|
||||
private:
|
||||
ProjectItem* unparentChild(ProjectItem* child);
|
||||
|
||||
boost::filesystem::path m_path;
|
||||
ProjectItemType m_type;
|
||||
bool m_typeLoaded;
|
||||
ProjectItem* m_parent;
|
||||
std::vector<ProjectItem*> m_children;
|
||||
bool m_childrenLoaded;
|
||||
Project* m_project;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // PROJECTITEM_H
|
@ -1,60 +0,0 @@
|
||||
import time
|
||||
from typing import List
|
||||
|
||||
class RecentProject(object):
|
||||
|
||||
DICT_FIELDS = [ 'path', 'name', 'dateAccessed', 'pinned' ]
|
||||
|
||||
"""
|
||||
Creates new instance of RecentProject.
|
||||
|
||||
Args:
|
||||
name: name of the project
|
||||
path: path to the project
|
||||
dateAccessed: date (unix time) when project was last accessed
|
||||
pinned: the project is pinned by the user
|
||||
|
||||
Remarks:
|
||||
If dateAccessed = None, it will be set to the current date and time.
|
||||
"""
|
||||
def __init__(self, name: str, path: str, dateAccessed : int = None, pinned : bool = False):
|
||||
self.name = name
|
||||
self.path = path
|
||||
self.dateAccessed = dateAccessed
|
||||
self.pinned = pinned
|
||||
|
||||
if (self.dateAccessed is None):
|
||||
self.dateAccessed = int(time.time())
|
||||
|
||||
"""
|
||||
Creates new instance of RecentProject from a dictionary.
|
||||
If the dateAccessed and pinned fields are strings, they are parsed.
|
||||
|
||||
Raises:
|
||||
ValueError if date cannot be parsed.
|
||||
"""
|
||||
@staticmethod
|
||||
def fromDictionary(data : dict) -> 'RecentProject':
|
||||
name = data['name']
|
||||
path = data['path']
|
||||
dateAccessed = data['dateAccessed']
|
||||
pinned = data['pinned']
|
||||
|
||||
if isinstance(dateAccessed, str):
|
||||
dateAccessed = int(dateAccessed)
|
||||
|
||||
if isinstance(pinned, str):
|
||||
pinned = pinned.lower() in [ 'yes', 'y', 'true', '1', 'on' ]
|
||||
|
||||
return RecentProject(name, path, dateAccessed, pinned)
|
||||
|
||||
"""
|
||||
Serialize to dictionary.
|
||||
"""
|
||||
def toDictionary(self) -> dict:
|
||||
return {
|
||||
"name" : self.name,
|
||||
"path" : self.path,
|
||||
"dateAccessed" : self.dateAccessed,
|
||||
"pinned" : self.pinned
|
||||
}
|
14
properties/config.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#define EMBER_PROJECT_EXTENSION ".emproj"
|
||||
#define EMBER_COMPOSITION_EXTENSION ".emcomp"
|
||||
#define EMBER_SEQUENCE_EXTENSION ".emseq"
|
||||
|
||||
#define APP_NAME "Ember"
|
||||
#define APP_VERSION "1.0"
|
||||
#define APP_AUTHOR "Tiberiu Chibici"
|
||||
|
||||
#define RECENT_PROJECTS_FILENAME "recentprojects.xml"
|
||||
|
||||
#endif // CONFIG_H
|
@ -1,14 +0,0 @@
|
||||
APP_NAME = "Ember"
|
||||
APP_VERSION = "1.0"
|
||||
APP_AUTHOR = "Tiberiu Chibici"
|
||||
APP_AUTHOR_SHORT = "TibiCh"
|
||||
|
||||
PROJECT_EXTENSION = "emproj"
|
||||
SEQUENCE_EXTENSION = "emseq"
|
||||
COMPOSITION_EXTENSION = "emcomp"
|
||||
|
||||
DEBUG = 1
|
||||
DEBUG_SUPPORTED_IMAGE = [ "png", "jpg", "tiff", "jpeg" ]
|
||||
DEBUG_SUPPORTED_VIDEO = [ "mp4", "avi", "wmv", "mkv" ]
|
||||
DEBUG_SUPPORTED_AUDIO = [ "mp3", "wav", "ogg", "aiff", "flac" ]
|
||||
DEBUG_SUPPORTED_SUB = [ "srt" ]
|
@ -1,11 +1,8 @@
|
||||
<!DOCTYPE RCC>
|
||||
<RCC version="1.0">
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<!-- Fonts -->
|
||||
<file alias="FontAwesome-Solid.otf">fonts/FontAwesomeSolid-5.2.otf</file>
|
||||
<file alias="FontAwesome-Regular.otf">fonts/FontAwesomeRegular-5.2.otf</file>
|
||||
|
||||
<!-- Picture -->
|
||||
<file alias="WelcomeBanner.jpg">img/charcoal-2396754.jpg</file>
|
||||
<file alias="Unused-WelcomeBanner.jpg">img/spark-959254.jpg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
</RCC>
|
Before Width: | Height: | Size: 3.0 MiB After Width: | Height: | Size: 3.0 MiB |
Before Width: | Height: | Size: 5.7 MiB After Width: | Height: | Size: 5.7 MiB |
238313
resources_rc.py
@ -1 +0,0 @@
|
||||
from .appdata_storage import AppDataStorage
|
@ -1,63 +0,0 @@
|
||||
import appdirs
|
||||
import os
|
||||
from configparser import ConfigParser
|
||||
import csv
|
||||
from typing import Iterable
|
||||
|
||||
from properties import config
|
||||
from model import RecentProject
|
||||
|
||||
class AppDataStorage(object):
|
||||
|
||||
SETTINGS_FILE = "config.ini"
|
||||
RECENT_PROJECTS_FILE = "recent.cfg"
|
||||
|
||||
def __init__(self):
|
||||
self.__appDataPath = appdirs.user_data_dir(config.APP_NAME, config.APP_AUTHOR_SHORT, config.APP_VERSION)
|
||||
self.__settingsPath = os.path.join(self.__appDataPath, AppDataStorage.SETTINGS_FILE)
|
||||
self.__recentProjectsPath = os.path.join(self.__appDataPath, AppDataStorage.RECENT_PROJECTS_FILE)
|
||||
|
||||
# make missing dirs in path
|
||||
def __createPath(self, path):
|
||||
dir = os.path.dirname(path)
|
||||
os.makedirs(dir, exist_ok=True)
|
||||
|
||||
def readSettings(self):
|
||||
if (os.path.exists(self.__settingsPath)):
|
||||
parser = ConfigParser()
|
||||
parser.read(self.__settingsPath)
|
||||
# todo: finish this
|
||||
|
||||
def writeSettings(self, settings):
|
||||
pass # todo: finish this
|
||||
|
||||
"""
|
||||
Reads recent projects list.
|
||||
|
||||
Returns:
|
||||
list of recent projects
|
||||
"""
|
||||
def readRecentProjects(self) -> Iterable[RecentProject]:
|
||||
if (os.path.exists(self.__recentProjectsPath)):
|
||||
with open(self.__recentProjectsPath, 'r') as recentProjectsFile:
|
||||
csvreader = csv.DictReader(recentProjectsFile, fieldnames=RecentProject.DICT_FIELDS)
|
||||
for row in csvreader:
|
||||
try:
|
||||
yield RecentProject.fromDictionary(row)
|
||||
except ValueError:
|
||||
print("Recent projects parse error - invalid date.", row)
|
||||
except KeyError:
|
||||
print("Recent projects parse error - fields not valid.", row)
|
||||
|
||||
"""
|
||||
Writes a list of recent projects.
|
||||
|
||||
Args:
|
||||
items: list of RecentProjects
|
||||
"""
|
||||
def writeRecentProjects(self, items : Iterable[RecentProject]) -> None:
|
||||
self.__createPath(self.__recentProjectsPath)
|
||||
with open(self.__recentProjectsPath, 'w') as recentProjectsFile:
|
||||
csvwriter = csv.DictWriter(recentProjectsFile, RecentProject.DICT_FIELDS)
|
||||
for item in items:
|
||||
csvwriter.writerow(item.toDictionary())
|
61
storage/appdatastorage.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <QStandardPaths>
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#include <properties/config.h>
|
||||
#include "appdatastorage.h"
|
||||
|
||||
namespace Ember
|
||||
{
|
||||
|
||||
AppDataStorage::AppDataStorage()
|
||||
{
|
||||
QString appData = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
m_appData = appData.toStdString();
|
||||
m_recentProjects = m_appData;
|
||||
m_recentProjects += RECENT_PROJECTS_FILENAME;
|
||||
}
|
||||
|
||||
void AppDataStorage::readRecentProjects(std::vector<RecentProject> &projects)
|
||||
{
|
||||
if (boost::filesystem::exists(m_recentProjects))
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
doc.load_file(m_recentProjects.c_str());
|
||||
|
||||
for (auto& node : doc.document_element())
|
||||
{
|
||||
if (strcmp(node.name(), "recentProject") == 0)
|
||||
{
|
||||
RecentProject recent;
|
||||
recent.name = node.attribute("name").as_string();
|
||||
recent.path = node.attribute("path").as_string();
|
||||
recent.access = static_cast<time_t>(node.attribute("access").as_llong());
|
||||
recent.pinned = node.attribute("pinned").as_bool();
|
||||
projects.push_back(recent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AppDataStorage::storeRecentProjects(const std::vector<RecentProject> &projects)
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
doc.append_child(pugi::node_declaration);
|
||||
|
||||
auto root = doc.append_child("recentProjects");
|
||||
for (RecentProject recent : projects)
|
||||
{
|
||||
auto node = root.append_child("recentProject");
|
||||
node.append_attribute("name").set_value(recent.name.c_str());
|
||||
node.append_attribute("path").set_value(recent.path.c_str());
|
||||
node.append_attribute("access").set_value(static_cast<long long>(recent.access));
|
||||
node.append_attribute("pinned").set_value(recent.pinned);
|
||||
}
|
||||
|
||||
// Save file, ensure directory exists
|
||||
boost::filesystem::create_directories(m_appData);
|
||||
doc.save_file(m_recentProjects.string().c_str());
|
||||
}
|
||||
|
||||
}
|
35
storage/appdatastorage.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef APPDATASTORAGE_H
|
||||
#define APPDATASTORAGE_H
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include <model/project.h>
|
||||
|
||||
namespace Ember
|
||||
{
|
||||
|
||||
class AppDataStorage
|
||||
{
|
||||
public:
|
||||
AppDataStorage();
|
||||
|
||||
/**
|
||||
* @brief Reads recent projects
|
||||
* @param projects List will be saved to given vector.
|
||||
*/
|
||||
void readRecentProjects(std::vector<RecentProject>& projects);
|
||||
|
||||
/**
|
||||
* @brief Stores recent projects
|
||||
* @param projects List of projects.
|
||||
*/
|
||||
void storeRecentProjects(const std::vector<RecentProject>& projects);
|
||||
|
||||
private:
|
||||
boost::filesystem::path m_appData;
|
||||
boost::filesystem::path m_recentProjects;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // APPDATASTORAGE_H
|
@ -1,59 +0,0 @@
|
||||
import os
|
||||
import json
|
||||
from typing import Iterable
|
||||
from model import Project, ProjectItem, ItemType
|
||||
from properties import config
|
||||
|
||||
def loadProject(self, projFilePath) -> Project:
|
||||
p = Project(projFilePath, self)
|
||||
p.rootDir = os.path.dirname(projFilePath)
|
||||
|
||||
|
||||
def saveProject(self, project : Project):
|
||||
pass
|
||||
|
||||
"""
|
||||
Determines the type of the project item.
|
||||
"""
|
||||
def getItemType(self, projectItem : ProjectItem) -> ItemType:
|
||||
path = projectItem.absolutePath()
|
||||
|
||||
if os.path.isdir(path):
|
||||
return ItemType.DIRECTORY
|
||||
|
||||
elif os.path.isfile(path):
|
||||
_, ext = os.path.splitext(path)
|
||||
ext = ext.lower().lstrip('.') # remove leading .
|
||||
|
||||
if ext == config.PROJECT_EXTENSION:
|
||||
return ItemType.PROJECT
|
||||
|
||||
elif ext == config.SEQUENCE_EXTENSION:
|
||||
return ItemType.SEQUENCE
|
||||
|
||||
elif ext == config.COMPOSITION_EXTENSION:
|
||||
return ItemType.COMPOSITION
|
||||
|
||||
elif ext in config.DEBUG_SUPPORTED_AUDIO:
|
||||
return ItemType.AUDIO
|
||||
|
||||
elif ext in config.DEBUG_SUPPORTED_VIDEO:
|
||||
return ItemType.VIDEO
|
||||
|
||||
elif ext in config.DEBUG_SUPPORTED_IMAGE:
|
||||
return ItemType.IMAGE
|
||||
|
||||
elif ext in config.DEBUG_SUPPORTED_SUB:
|
||||
return ItemType.SUBTITLES
|
||||
|
||||
return ItemType.UNKNOWN
|
||||
|
||||
|
||||
def getItemChildren(self, projectItem : ProjectItem) -> Iterable[ProjectItem]:
|
||||
if projectItem.itemType() == ItemType.DIRECTORY:
|
||||
for item in os.listdir(projectItem.absolutePath()):
|
||||
yield ProjectItem(item, projectItem.project, self, projectItem)
|
||||
|
||||
def getProjectItems(self, project : Project) -> Iterable[ProjectItem]:
|
||||
for item in os.listdir(project.rootDir):
|
||||
yield ProjectItem(item, project, self, None)
|
3
todo.txt
Normal file
@ -0,0 +1,3 @@
|
||||
Project items:
|
||||
* add setting(?) - delete to recycle bin, or permanent
|
||||
* type detection - use FFMPEG to detect properly
|
14
ui/dialogs/newprojectdialog.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "newprojectdialog.h"
|
||||
#include "ui_newprojectdialog.h"
|
||||
|
||||
NewProjectDialog::NewProjectDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::NewProjectDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
NewProjectDialog::~NewProjectDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
22
ui/dialogs/newprojectdialog.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef NEWPROJECTDIALOG_H
|
||||
#define NEWPROJECTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class NewProjectDialog;
|
||||
}
|
||||
|
||||
class NewProjectDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NewProjectDialog(QWidget *parent = 0);
|
||||
~NewProjectDialog();
|
||||
|
||||
private:
|
||||
Ui::NewProjectDialog *ui;
|
||||
};
|
||||
|
||||
#endif // NEWPROJECTDIALOG_H
|
@ -1,26 +0,0 @@
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from PyQt5.QtGui import QFontDatabase
|
||||
from ui.main_window import MainWindow
|
||||
from business.project_manager import ProjectManager
|
||||
from storage.appdata_storage import AppDataStorage
|
||||
|
||||
class EmberApplication(QApplication):
|
||||
|
||||
def __init__(self, argv):
|
||||
super().__init__(argv)
|
||||
|
||||
def start(self):
|
||||
|
||||
QFontDatabase.addApplicationFont(":/FontAwesome-Solid.otf")
|
||||
QFontDatabase.addApplicationFont(":/FontAwesome-Regular.otf")
|
||||
|
||||
# setup resources
|
||||
appDataStorage = AppDataStorage()
|
||||
projectManager = ProjectManager(appDataStorage)
|
||||
#projectManager.debug_populateRecentProjects()
|
||||
|
||||
# show main window
|
||||
mainwindow = MainWindow(projectManager)
|
||||
mainwindow.show()
|
||||
|
||||
exit(self.exec_())
|
@ -1,54 +0,0 @@
|
||||
import traceback
|
||||
from PyQt5.QtWidgets import QMainWindow, QVBoxLayout, QWidget, QApplication, QMessageBox
|
||||
from PyQt5.QtCore import QTimer
|
||||
|
||||
from model import Project
|
||||
from business import ProjectManager
|
||||
from ui.project.project_panel import ProjectPanel
|
||||
from ui.project.new_project_dialog import NewProjectDialog
|
||||
from ui.welcome.welcome_dialog import WelcomeDialog
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
|
||||
def __init__(self, projectManager : ProjectManager):
|
||||
super().__init__()
|
||||
self.__projectManager = projectManager
|
||||
self.setupUi()
|
||||
QTimer.singleShot(0, self.showWelcomeDialog)
|
||||
|
||||
def setupUi(self):
|
||||
self.projectPanel = ProjectPanel()
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.addWidget(self.projectPanel)
|
||||
|
||||
cwidget = QWidget()
|
||||
cwidget.setLayout(layout)
|
||||
|
||||
self.setCentralWidget(cwidget)
|
||||
|
||||
def showWelcomeDialog(self):
|
||||
# Show welcome dialog
|
||||
welcome = WelcomeDialog(self.__projectManager, self)
|
||||
if welcome.exec() > 0:
|
||||
if welcome.resultAction == WelcomeDialog.NEW_PROJECT:
|
||||
self.newProject()
|
||||
elif welcome.resultAction == WelcomeDialog.OPEN_PROJECT:
|
||||
self.openProject(welcome.projectToOpen)
|
||||
|
||||
def newProject(self):
|
||||
dialog = NewProjectDialog(self.__projectManager, self)
|
||||
if dialog.exec() > 0:
|
||||
self.loadProject(dialog.project)
|
||||
|
||||
def openProject(self, projectPath : str = None):
|
||||
try:
|
||||
project = self.__projectManager.openProject(projectPath)
|
||||
except Exception as ex:
|
||||
print("Failed to open project: ", traceback.format_exc())
|
||||
QMessageBox.critical(self,
|
||||
self.tr('An error occurred'),
|
||||
self.tr('The project could not be open.'))
|
||||
|
||||
def loadProject(self, project : Project):
|
||||
pass
|
11
ui/mainwindow.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent)
|
||||
{
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
}
|
18
ui/mainwindow.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
@ -1,61 +0,0 @@
|
||||
import traceback
|
||||
from PyQt5.QtWidgets import QDialog, QWidget, QDialogButtonBox, QFileDialog, QPushButton, QMessageBox
|
||||
|
||||
from model import Project
|
||||
from business import ProjectManager
|
||||
from properties.config import PROJECT_EXTENSION
|
||||
from ui.project.new_project_dialog_ui import Ui_NewProjectDialog
|
||||
|
||||
class NewProjectDialog(QDialog):
|
||||
|
||||
def __init__(self, projectManager : ProjectManager, parent : QWidget = None):
|
||||
super().__init__(parent)
|
||||
self.__projectManager = projectManager
|
||||
self.project : Project = None
|
||||
self.setupUi()
|
||||
self.setupActions()
|
||||
self.validate()
|
||||
|
||||
def setupUi(self):
|
||||
self.ui = Ui_NewProjectDialog()
|
||||
self.ui.setupUi(self)
|
||||
self.__buttonOk : QPushButton = self.ui.buttonBox.button(QDialogButtonBox.Ok)
|
||||
|
||||
def setupActions(self):
|
||||
self.ui.buttonBrowse.pressed.connect(self.browsePressed)
|
||||
self.ui.textProjectPath.textChanged.connect(self.projectPathChanged)
|
||||
self.ui.buttonBox.accepted.connect(self.okPressed)
|
||||
|
||||
def validate(self):
|
||||
valid = True
|
||||
if not self.ui.textProjectPath.text():
|
||||
valid = False
|
||||
self.__buttonOk.setEnabled(valid)
|
||||
|
||||
def browsePressed(self):
|
||||
path = QFileDialog.getSaveFileName(
|
||||
self,
|
||||
self.tr('New project'),
|
||||
'',
|
||||
self.tr(f'Project files (*.{PROJECT_EXTENSION});;All files (*.*)'))
|
||||
|
||||
if path[0]:
|
||||
self.ui.textProjectPath.setText(path[0])
|
||||
|
||||
def projectPathChanged(self):
|
||||
self.validate()
|
||||
|
||||
def okPressed(self):
|
||||
try:
|
||||
path = self.ui.textProjectPath.text()
|
||||
self.project = self.__projectManager.newProject(path)
|
||||
self.accept()
|
||||
|
||||
except Exception as ex:
|
||||
print("Failed to create project: ", traceback.format_exc())
|
||||
QMessageBox.critical(self,
|
||||
self.tr('An error occurred'),
|
||||
self.tr('The project could not be created in the given location.'))
|
||||
|
||||
|
||||
|
@ -1,124 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'ui/project/new_project_dialog.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.10.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
class Ui_NewProjectDialog(object):
|
||||
def setupUi(self, NewProjectDialog):
|
||||
NewProjectDialog.setObjectName("NewProjectDialog")
|
||||
NewProjectDialog.resize(486, 299)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(NewProjectDialog)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self._panelProjectPath = QtWidgets.QWidget(NewProjectDialog)
|
||||
self._panelProjectPath.setObjectName("_panelProjectPath")
|
||||
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self._panelProjectPath)
|
||||
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
||||
self.labelProjectPath = QtWidgets.QLabel(self._panelProjectPath)
|
||||
self.labelProjectPath.setObjectName("labelProjectPath")
|
||||
self.horizontalLayout_3.addWidget(self.labelProjectPath)
|
||||
self.textProjectPath = QtWidgets.QLineEdit(self._panelProjectPath)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.textProjectPath.sizePolicy().hasHeightForWidth())
|
||||
self.textProjectPath.setSizePolicy(sizePolicy)
|
||||
self.textProjectPath.setObjectName("textProjectPath")
|
||||
self.horizontalLayout_3.addWidget(self.textProjectPath)
|
||||
self.buttonBrowse = QtWidgets.QPushButton(self._panelProjectPath)
|
||||
self.buttonBrowse.setObjectName("buttonBrowse")
|
||||
self.horizontalLayout_3.addWidget(self.buttonBrowse)
|
||||
self.verticalLayout.addWidget(self._panelProjectPath)
|
||||
self.groupVideoSettings = QtWidgets.QGroupBox(NewProjectDialog)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(2)
|
||||
sizePolicy.setHeightForWidth(self.groupVideoSettings.sizePolicy().hasHeightForWidth())
|
||||
self.groupVideoSettings.setSizePolicy(sizePolicy)
|
||||
self.groupVideoSettings.setObjectName("groupVideoSettings")
|
||||
self.formlayout = QtWidgets.QFormLayout(self.groupVideoSettings)
|
||||
self.formlayout.setContentsMargins(20, -1, 20, -1)
|
||||
self.formlayout.setObjectName("formlayout")
|
||||
self.labelColorDepth = QtWidgets.QLabel(self.groupVideoSettings)
|
||||
self.labelColorDepth.setMinimumSize(QtCore.QSize(110, 0))
|
||||
self.labelColorDepth.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||
self.labelColorDepth.setObjectName("labelColorDepth")
|
||||
self.formlayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.labelColorDepth)
|
||||
self.comboColorDepth = QtWidgets.QComboBox(self.groupVideoSettings)
|
||||
self.comboColorDepth.setMinimumSize(QtCore.QSize(150, 0))
|
||||
self.comboColorDepth.setObjectName("comboColorDepth")
|
||||
self.comboColorDepth.addItem("")
|
||||
self.comboColorDepth.addItem("")
|
||||
self.comboColorDepth.addItem("")
|
||||
self.formlayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.comboColorDepth)
|
||||
self.labelColorSpace = QtWidgets.QLabel(self.groupVideoSettings)
|
||||
self.labelColorSpace.setMinimumSize(QtCore.QSize(110, 0))
|
||||
self.labelColorSpace.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||
self.labelColorSpace.setObjectName("labelColorSpace")
|
||||
self.formlayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.labelColorSpace)
|
||||
self.comboColorSpace = QtWidgets.QComboBox(self.groupVideoSettings)
|
||||
self.comboColorSpace.setObjectName("comboColorSpace")
|
||||
self.comboColorSpace.addItem("")
|
||||
self.formlayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.comboColorSpace)
|
||||
self.verticalLayout.addWidget(self.groupVideoSettings)
|
||||
self.groupAudioSettings = QtWidgets.QGroupBox(NewProjectDialog)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(1)
|
||||
sizePolicy.setHeightForWidth(self.groupAudioSettings.sizePolicy().hasHeightForWidth())
|
||||
self.groupAudioSettings.setSizePolicy(sizePolicy)
|
||||
self.groupAudioSettings.setObjectName("groupAudioSettings")
|
||||
self.formLayout_2 = QtWidgets.QFormLayout(self.groupAudioSettings)
|
||||
self.formLayout_2.setContentsMargins(20, -1, 20, -1)
|
||||
self.formLayout_2.setObjectName("formLayout_2")
|
||||
self.labelSampleRate = QtWidgets.QLabel(self.groupAudioSettings)
|
||||
self.labelSampleRate.setMinimumSize(QtCore.QSize(110, 0))
|
||||
self.labelSampleRate.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||
self.labelSampleRate.setObjectName("labelSampleRate")
|
||||
self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.labelSampleRate)
|
||||
self.comboSampleRate = QtWidgets.QComboBox(self.groupAudioSettings)
|
||||
self.comboSampleRate.setObjectName("comboSampleRate")
|
||||
self.comboSampleRate.addItem("")
|
||||
self.comboSampleRate.addItem("")
|
||||
self.comboSampleRate.addItem("")
|
||||
self.comboSampleRate.addItem("")
|
||||
self.comboSampleRate.addItem("")
|
||||
self.comboSampleRate.addItem("")
|
||||
self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.comboSampleRate)
|
||||
self.verticalLayout.addWidget(self.groupAudioSettings)
|
||||
self.buttonBox = QtWidgets.QDialogButtonBox(NewProjectDialog)
|
||||
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||
self.buttonBox.setObjectName("buttonBox")
|
||||
self.verticalLayout.addWidget(self.buttonBox)
|
||||
|
||||
self.retranslateUi(NewProjectDialog)
|
||||
self.comboSampleRate.setCurrentIndex(2)
|
||||
self.buttonBox.rejected.connect(NewProjectDialog.reject)
|
||||
QtCore.QMetaObject.connectSlotsByName(NewProjectDialog)
|
||||
|
||||
def retranslateUi(self, NewProjectDialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
NewProjectDialog.setWindowTitle(_translate("NewProjectDialog", "New project"))
|
||||
self.labelProjectPath.setText(_translate("NewProjectDialog", "Project path:"))
|
||||
self.buttonBrowse.setText(_translate("NewProjectDialog", "&Browse..."))
|
||||
self.groupVideoSettings.setTitle(_translate("NewProjectDialog", "Video settings"))
|
||||
self.labelColorDepth.setText(_translate("NewProjectDialog", "Color depth:"))
|
||||
self.comboColorDepth.setItemText(0, _translate("NewProjectDialog", "8 bits/channel"))
|
||||
self.comboColorDepth.setItemText(1, _translate("NewProjectDialog", "16 bits/channel"))
|
||||
self.comboColorDepth.setItemText(2, _translate("NewProjectDialog", "32 bits/channel (float)"))
|
||||
self.labelColorSpace.setText(_translate("NewProjectDialog", "Color space:"))
|
||||
self.comboColorSpace.setItemText(0, _translate("NewProjectDialog", "sRGB"))
|
||||
self.groupAudioSettings.setTitle(_translate("NewProjectDialog", "Audio settings"))
|
||||
self.labelSampleRate.setText(_translate("NewProjectDialog", "Sample rate:"))
|
||||
self.comboSampleRate.setItemText(0, _translate("NewProjectDialog", "32 kHz"))
|
||||
self.comboSampleRate.setItemText(1, _translate("NewProjectDialog", "44.1 kHz"))
|
||||
self.comboSampleRate.setItemText(2, _translate("NewProjectDialog", "48 kHz"))
|
||||
self.comboSampleRate.setItemText(3, _translate("NewProjectDialog", "88.2 kHz"))
|
||||
self.comboSampleRate.setItemText(4, _translate("NewProjectDialog", "96 kHz"))
|
||||
self.comboSampleRate.setItemText(5, _translate("NewProjectDialog", "192 kHz"))
|
||||
|
@ -1,12 +0,0 @@
|
||||
from PyQt5 import QtCore, QtWidgets, uic
|
||||
import os
|
||||
|
||||
from ui.project.project_panel_ui import Ui_Form
|
||||
|
||||
class ProjectPanel(QtWidgets.QWidget):
|
||||
|
||||
def __init__(self):
|
||||
super(ProjectPanel, self).__init__()
|
||||
self.ui = Ui_Form()
|
||||
self.ui.setupUi(self)
|
||||
|
@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>90</x>
|
||||
<y>40</y>
|
||||
<width>84</width>
|
||||
<height>34</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="checkBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>80</x>
|
||||
<y>150</y>
|
||||
<width>86</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>CheckBox</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="checkBox_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>80</x>
|
||||
<y>180</y>
|
||||
<width>86</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>CheckBox</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -1,34 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'ui/project/project_panel.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.10.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
class Ui_Form(object):
|
||||
def setupUi(self, Form):
|
||||
Form.setObjectName("Form")
|
||||
Form.resize(400, 300)
|
||||
self.pushButton = QtWidgets.QPushButton(Form)
|
||||
self.pushButton.setGeometry(QtCore.QRect(90, 40, 84, 34))
|
||||
self.pushButton.setObjectName("pushButton")
|
||||
self.checkBox = QtWidgets.QCheckBox(Form)
|
||||
self.checkBox.setGeometry(QtCore.QRect(80, 150, 86, 22))
|
||||
self.checkBox.setObjectName("checkBox")
|
||||
self.checkBox_2 = QtWidgets.QCheckBox(Form)
|
||||
self.checkBox_2.setGeometry(QtCore.QRect(80, 180, 86, 22))
|
||||
self.checkBox_2.setObjectName("checkBox_2")
|
||||
|
||||
self.retranslateUi(Form)
|
||||
QtCore.QMetaObject.connectSlotsByName(Form)
|
||||
|
||||
def retranslateUi(self, Form):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
Form.setWindowTitle(_translate("Form", "Form"))
|
||||
self.pushButton.setText(_translate("Form", "PushButton"))
|
||||
self.checkBox.setText(_translate("Form", "CheckBox"))
|
||||
self.checkBox_2.setText(_translate("Form", "CheckBox"))
|
||||
|
@ -1,175 +0,0 @@
|
||||
import traceback
|
||||
|
||||
from PyQt5.QtWidgets import QDialog, QCommandLinkButton, QListWidgetItem, QFileDialog, QWidget, QHBoxLayout, QLabel, QToolButton, QLayout
|
||||
from PyQt5.QtGui import QPixmap, QResizeEvent, QFont
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
|
||||
from ui.welcome.welcome_dialog_ui import Ui_WelcomeDialog
|
||||
from model import RecentProject
|
||||
from business import ProjectManager
|
||||
from properties.config import PROJECT_EXTENSION
|
||||
|
||||
|
||||
class RecentProjectListWidget(QWidget):
|
||||
|
||||
deleted = pyqtSignal()
|
||||
pinned = pyqtSignal(bool)
|
||||
|
||||
def __init__(self, recentProject : RecentProject):
|
||||
super().__init__()
|
||||
self.project = recentProject
|
||||
self.__selected = False
|
||||
self.setupUi()
|
||||
self.setupActions()
|
||||
self.__setProject(recentProject)
|
||||
self.setSelected(False)
|
||||
|
||||
def setupUi(self):
|
||||
layout = QHBoxLayout()
|
||||
|
||||
# label
|
||||
self.__text = QLabel()
|
||||
layout.addWidget(self.__text)
|
||||
|
||||
# space
|
||||
layout.addStretch()
|
||||
|
||||
# pin button
|
||||
self.__pin_button = QToolButton()
|
||||
self.__pin_button.setFont(QFont("Font Awesome 5 Free"))
|
||||
self.__pin_button.setText("\uf08d")
|
||||
self.__pin_button.setCheckable(True)
|
||||
layout.addWidget(self.__pin_button)
|
||||
|
||||
# delete button
|
||||
self.__del_button = QToolButton()
|
||||
self.__del_button.setFont(QFont("Font Awesome 5 Free"))
|
||||
self.__del_button.setText("\uf2ed")
|
||||
layout.addWidget(self.__del_button)
|
||||
|
||||
# pin indicator
|
||||
self.__pin_indicator = QLabel()
|
||||
self.__pin_indicator.setFont(QFont("Font Awesome 5 Free"))
|
||||
self.__pin_indicator.setText("\uf08d")
|
||||
layout.addWidget(self.__pin_indicator)
|
||||
|
||||
# done
|
||||
layout.setSizeConstraint(QLayout.SetMinimumSize)
|
||||
self.setLayout(layout)
|
||||
|
||||
def setupActions(self):
|
||||
self.__pin_button.toggled.connect(self.__pinnedToggled)
|
||||
self.__del_button.pressed.connect(self.deleted)
|
||||
|
||||
def setSelected(self, selected : bool = True):
|
||||
self.__selected = selected
|
||||
self.__pin_button.setVisible(selected)
|
||||
self.__del_button.setVisible(selected)
|
||||
self.__pin_indicator.setVisible(not self.__selected and self.__pin_button.isChecked())
|
||||
|
||||
def setPinned(self, isPinned : bool = True):
|
||||
self.__pin_button.setChecked(isPinned)
|
||||
|
||||
def __setProject(self, project : RecentProject):
|
||||
self.__text = project.name
|
||||
self.setPinned(project.pinned)
|
||||
|
||||
def __pinnedToggled(self, isPinned : bool):
|
||||
self.__pin_indicator.setVisible(not self.__selected and isPinned)
|
||||
if (isPinned != self.project.pinned):
|
||||
self.pinned.emit(isPinned)
|
||||
|
||||
|
||||
class WelcomeDialog(QDialog):
|
||||
NEW_PROJECT = 0
|
||||
OPEN_PROJECT = 1
|
||||
|
||||
def __init__(self, projectManager : ProjectManager, parent : QWidget = None):
|
||||
super().__init__(parent)
|
||||
self.__projectManager = projectManager
|
||||
self.resultAction : int = None
|
||||
self.projectToOpen : str = None
|
||||
self.setResult(QDialog.Rejected)
|
||||
self.setupUi()
|
||||
self.setupActions()
|
||||
self.populateRecentProjects()
|
||||
|
||||
def setupUi(self) -> None:
|
||||
self.ui = Ui_WelcomeDialog()
|
||||
self.ui.setupUi(self)
|
||||
self.image = QPixmap(self.ui.picture.pixmap())
|
||||
|
||||
def setupActions(self) -> None:
|
||||
self.ui.buttonNewProject.pressed.connect(self.newProjectPressed)
|
||||
self.ui.buttonOpenProject.pressed.connect(self.openProjectPressed)
|
||||
self.ui.listRecentProjects.currentItemChanged.connect(self.listRecentProjectsCurrentChanged)
|
||||
self.ui.listRecentProjects.itemActivated.connect(self.listRecentProjectsItemActivated)
|
||||
|
||||
def resizeEvent(self, event : QResizeEvent) -> None:
|
||||
super().resizeEvent(event)
|
||||
|
||||
picSize = self.ui.picture.size()
|
||||
pic = self.image.scaled(picSize, Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation)
|
||||
self.ui.picture.setPixmap(pic)
|
||||
|
||||
def populateRecentProjects(self) -> None:
|
||||
projects = list(self.__projectManager.getRecentProjects())
|
||||
projects = sorted(projects, key=lambda x: (x.pinned, x.dateAccessed), reverse=True)
|
||||
|
||||
for project in projects:
|
||||
|
||||
widget = RecentProjectListWidget(project)
|
||||
widget.pinned.connect(self.__projectPinned)
|
||||
widget.deleted.connect(self.__projectDeleted)
|
||||
|
||||
item = QListWidgetItem(project.name)
|
||||
item.setData(Qt.UserRole, project)
|
||||
item.setSizeHint(widget.sizeHint())
|
||||
|
||||
self.ui.listRecentProjects.addItem(item)
|
||||
self.ui.listRecentProjects.setItemWidget(item, widget)
|
||||
|
||||
def newProjectPressed(self) -> None:
|
||||
self.resultAction = WelcomeDialog.NEW_PROJECT
|
||||
self.accept()
|
||||
|
||||
def openProjectPressed(self) -> None:
|
||||
proj = QFileDialog.getOpenFileName(
|
||||
self,
|
||||
self.tr('Open project'),
|
||||
'',
|
||||
self.tr(f'Project files (*.{PROJECT_EXTENSION});;All files (*.*)'))
|
||||
|
||||
if proj[0]:
|
||||
self.projectToOpen = proj[0]
|
||||
self.resultAction = WelcomeDialog.OPEN_PROJECT
|
||||
self.accept()
|
||||
|
||||
def listRecentProjectsCurrentChanged(self, current : QListWidgetItem, previous : QListWidgetItem) -> None:
|
||||
if not current is None:
|
||||
RecentProjectListWidget = self.ui.listRecentProjects.itemWidget(current)
|
||||
RecentProjectListWidget.setSelected(True)
|
||||
|
||||
if not previous is None:
|
||||
RecentProjectListWidget = self.ui.listRecentProjects.itemWidget(previous)
|
||||
if not RecentProjectListWidget is None:
|
||||
RecentProjectListWidget.setSelected(False)
|
||||
|
||||
def listRecentProjectsItemActivated(self, item : QListWidgetItem) -> None:
|
||||
project : RecentProject = item.data(Qt.UserRole)
|
||||
self.resultAction = WelcomeDialog.OPEN_PROJECT
|
||||
self.projectToOpen = project.path
|
||||
self.accept()
|
||||
|
||||
def __projectPinned(self, isPinned : bool) -> None:
|
||||
project = self.ui.listRecentProjects.currentItem().data(Qt.UserRole)
|
||||
self.__projectManager.pinRecentProject(project, isPinned)
|
||||
|
||||
def __projectDeleted(self) -> None:
|
||||
project = self.ui.listRecentProjects.currentItem().data(Qt.UserRole)
|
||||
try:
|
||||
self.__projectManager.deleteRecentProject(project)
|
||||
self.ui.listRecentProjects.takeItem(self.ui.listRecentProjects.currentRow())
|
||||
except:
|
||||
print("Error while deleting! ")
|
||||
traceback.print_exc()
|
@ -1,90 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'ui/welcome/welcome_dialog.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.10.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
class Ui_WelcomeDialog(object):
|
||||
def setupUi(self, WelcomeDialog):
|
||||
WelcomeDialog.setObjectName("WelcomeDialog")
|
||||
WelcomeDialog.setWindowModality(QtCore.Qt.WindowModal)
|
||||
WelcomeDialog.resize(667, 601)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(WelcomeDialog)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.picture = QtWidgets.QLabel(WelcomeDialog)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.picture.sizePolicy().hasHeightForWidth())
|
||||
self.picture.setSizePolicy(sizePolicy)
|
||||
self.picture.setMinimumSize(QtCore.QSize(0, 120))
|
||||
self.picture.setMaximumSize(QtCore.QSize(16777215, 120))
|
||||
self.picture.setText("")
|
||||
self.picture.setPixmap(QtGui.QPixmap(":/WelcomeBanner.jpg"))
|
||||
self.picture.setScaledContents(False)
|
||||
self.picture.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.picture.setObjectName("picture")
|
||||
self.verticalLayout.addWidget(self.picture)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.panelRecentProjects = QtWidgets.QWidget(WelcomeDialog)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(2)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.panelRecentProjects.sizePolicy().hasHeightForWidth())
|
||||
self.panelRecentProjects.setSizePolicy(sizePolicy)
|
||||
self.panelRecentProjects.setObjectName("panelRecentProjects")
|
||||
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.panelRecentProjects)
|
||||
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
||||
self.labelRecentProjects = QtWidgets.QLabel(self.panelRecentProjects)
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(13)
|
||||
self.labelRecentProjects.setFont(font)
|
||||
self.labelRecentProjects.setObjectName("labelRecentProjects")
|
||||
self.verticalLayout_3.addWidget(self.labelRecentProjects)
|
||||
self.listRecentProjects = QtWidgets.QListWidget(self.panelRecentProjects)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(1)
|
||||
sizePolicy.setHeightForWidth(self.listRecentProjects.sizePolicy().hasHeightForWidth())
|
||||
self.listRecentProjects.setSizePolicy(sizePolicy)
|
||||
self.listRecentProjects.setObjectName("listRecentProjects")
|
||||
self.verticalLayout_3.addWidget(self.listRecentProjects)
|
||||
self.horizontalLayout.addWidget(self.panelRecentProjects)
|
||||
self.panelButtons = QtWidgets.QWidget(WelcomeDialog)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(1)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.panelButtons.sizePolicy().hasHeightForWidth())
|
||||
self.panelButtons.setSizePolicy(sizePolicy)
|
||||
self.panelButtons.setObjectName("panelButtons")
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.panelButtons)
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||
self.verticalLayout_2.addItem(spacerItem)
|
||||
self.buttonNewProject = QtWidgets.QCommandLinkButton(self.panelButtons)
|
||||
self.buttonNewProject.setObjectName("buttonNewProject")
|
||||
self.verticalLayout_2.addWidget(self.buttonNewProject)
|
||||
self.buttonOpenProject = QtWidgets.QCommandLinkButton(self.panelButtons)
|
||||
self.buttonOpenProject.setObjectName("buttonOpenProject")
|
||||
self.verticalLayout_2.addWidget(self.buttonOpenProject)
|
||||
self.horizontalLayout.addWidget(self.panelButtons)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
|
||||
self.retranslateUi(WelcomeDialog)
|
||||
QtCore.QMetaObject.connectSlotsByName(WelcomeDialog)
|
||||
|
||||
def retranslateUi(self, WelcomeDialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
WelcomeDialog.setWindowTitle(_translate("WelcomeDialog", "Welcome"))
|
||||
self.labelRecentProjects.setText(_translate("WelcomeDialog", "Recent projects"))
|
||||
self.buttonNewProject.setText(_translate("WelcomeDialog", "&New Project"))
|
||||
self.buttonNewProject.setDescription(_translate("WelcomeDialog", "Create a new project."))
|
||||
self.buttonOpenProject.setText(_translate("WelcomeDialog", "&Open Project"))
|
||||
self.buttonOpenProject.setDescription(_translate("WelcomeDialog", "Open an existing project."))
|
||||
|
||||
import resources_rc
|