2015-04-01 04:31:45 +08:00
|
|
|
// Aseprite
|
|
|
|
|
// Copyright (C) 2001-2015 David Capello
|
|
|
|
|
//
|
|
|
|
|
// This program is free software; you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU General Public License version 2 as
|
|
|
|
|
// published by the Free Software Foundation.
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "app/ui/workspace_panel.h"
|
|
|
|
|
|
|
|
|
|
#include "app/ui/skin/skin_theme.h"
|
|
|
|
|
#include "app/ui/workspace.h"
|
|
|
|
|
#include "app/ui/workspace_tabs.h"
|
|
|
|
|
#include "app/ui/workspace_view.h"
|
|
|
|
|
#include "base/remove_from_container.h"
|
|
|
|
|
#include "ui/box.h"
|
|
|
|
|
#include "ui/paint_event.h"
|
|
|
|
|
#include "ui/resize_event.h"
|
|
|
|
|
#include "ui/splitter.h"
|
|
|
|
|
|
|
|
|
|
namespace app {
|
|
|
|
|
|
|
|
|
|
#define ANI_DROPAREA_TICKS 4
|
|
|
|
|
|
|
|
|
|
using namespace app::skin;
|
|
|
|
|
using namespace ui;
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
WidgetType WorkspacePanel::Type()
|
|
|
|
|
{
|
|
|
|
|
static WidgetType type = kGenericWidget;
|
|
|
|
|
if (type == kGenericWidget)
|
|
|
|
|
type = register_widget_type();
|
|
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WorkspacePanel::WorkspacePanel(PanelType panelType)
|
|
|
|
|
: Widget(WorkspacePanel::Type())
|
|
|
|
|
, m_panelType(panelType)
|
|
|
|
|
, m_tabs(nullptr)
|
|
|
|
|
, m_activeView(nullptr)
|
|
|
|
|
, m_dropArea(0)
|
|
|
|
|
, m_leftTime(0)
|
|
|
|
|
, m_rightTime(0)
|
|
|
|
|
, m_topTime(0)
|
|
|
|
|
, m_bottomTime(0)
|
|
|
|
|
{
|
|
|
|
|
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
|
|
|
|
|
setBgColor(theme->colors.workspace());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WorkspacePanel::~WorkspacePanel()
|
|
|
|
|
{
|
|
|
|
|
// No views at this point.
|
|
|
|
|
ASSERT(m_views.empty());
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-03 02:43:50 +08:00
|
|
|
void WorkspacePanel::setTabsBar(WorkspaceTabs* tabs)
|
2015-04-01 04:31:45 +08:00
|
|
|
{
|
|
|
|
|
m_tabs = tabs;
|
2015-04-03 02:43:50 +08:00
|
|
|
m_tabs->setPanel(this);
|
2015-04-01 04:31:45 +08:00
|
|
|
}
|
|
|
|
|
|
2015-04-05 03:45:58 +08:00
|
|
|
void WorkspacePanel::addView(WorkspaceView* view, bool from_drop, int pos)
|
2015-04-01 04:31:45 +08:00
|
|
|
{
|
|
|
|
|
if (pos < 0)
|
|
|
|
|
m_views.push_back(view);
|
|
|
|
|
else
|
|
|
|
|
m_views.insert(m_views.begin()+pos, view);
|
|
|
|
|
|
|
|
|
|
if (m_tabs)
|
2015-04-05 03:45:58 +08:00
|
|
|
m_tabs->addTab(dynamic_cast<TabView*>(view), from_drop, pos);
|
2015-04-01 04:31:45 +08:00
|
|
|
|
|
|
|
|
// Insert the view content as a hidden widget.
|
|
|
|
|
Widget* content = view->getContentWidget();
|
|
|
|
|
content->setVisible(false);
|
|
|
|
|
addChild(content);
|
|
|
|
|
|
|
|
|
|
setActiveView(view);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorkspacePanel::removeView(WorkspaceView* view)
|
|
|
|
|
{
|
|
|
|
|
base::remove_from_container(m_views, view);
|
|
|
|
|
|
|
|
|
|
Widget* content = view->getContentWidget();
|
|
|
|
|
ASSERT(hasChild(content));
|
|
|
|
|
removeChild(content);
|
|
|
|
|
|
|
|
|
|
// Remove related tab.
|
|
|
|
|
if (m_tabs) {
|
|
|
|
|
m_tabs->removeTab(dynamic_cast<TabView*>(view), true);
|
|
|
|
|
|
|
|
|
|
// The selected
|
|
|
|
|
TabView* tabView = m_tabs->getSelectedTab();
|
|
|
|
|
view = dynamic_cast<WorkspaceView*>(tabView);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
view = nullptr;
|
|
|
|
|
|
|
|
|
|
setActiveView(view);
|
|
|
|
|
if (!view)
|
|
|
|
|
getWorkspace()->setMainPanelAsActive();
|
|
|
|
|
|
|
|
|
|
// Destroy this panel
|
|
|
|
|
if (m_views.empty() && m_panelType == SUB_PANEL) {
|
|
|
|
|
Widget* self = getParent();
|
2015-06-24 01:37:22 +08:00
|
|
|
ASSERT(self->type() == kBoxWidget);
|
2015-04-01 04:31:45 +08:00
|
|
|
|
|
|
|
|
Widget* splitter = self->getParent();
|
2015-06-24 01:37:22 +08:00
|
|
|
ASSERT(splitter->type() == kSplitterWidget);
|
2015-04-01 04:31:45 +08:00
|
|
|
|
|
|
|
|
Widget* parent = splitter->getParent();
|
|
|
|
|
|
|
|
|
|
Widget* side =
|
|
|
|
|
(splitter->getFirstChild() == self ?
|
|
|
|
|
splitter->getLastChild():
|
|
|
|
|
splitter->getFirstChild());
|
|
|
|
|
|
|
|
|
|
splitter->removeChild(side);
|
|
|
|
|
parent->replaceChild(splitter, side);
|
|
|
|
|
|
|
|
|
|
self->deferDelete();
|
|
|
|
|
|
|
|
|
|
parent->layout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WorkspaceView* WorkspacePanel::activeView()
|
|
|
|
|
{
|
|
|
|
|
return m_activeView;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorkspacePanel::setActiveView(WorkspaceView* view)
|
|
|
|
|
{
|
|
|
|
|
m_activeView = view;
|
|
|
|
|
|
|
|
|
|
for (auto v : m_views)
|
|
|
|
|
v->getContentWidget()->setVisible(v == view);
|
|
|
|
|
|
|
|
|
|
if (m_tabs && view)
|
|
|
|
|
m_tabs->selectTab(dynamic_cast<TabView*>(view));
|
|
|
|
|
|
|
|
|
|
adjustActiveViewBounds();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorkspacePanel::onPaint(PaintEvent& ev)
|
|
|
|
|
{
|
|
|
|
|
ev.getGraphics()->fillRect(getBgColor(), getClientBounds());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorkspacePanel::onResize(ui::ResizeEvent& ev)
|
|
|
|
|
{
|
|
|
|
|
setBoundsQuietly(ev.getBounds());
|
|
|
|
|
adjustActiveViewBounds();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorkspacePanel::adjustActiveViewBounds()
|
|
|
|
|
{
|
|
|
|
|
gfx::Rect rc = getChildrenBounds();
|
|
|
|
|
|
|
|
|
|
// Preview to drop tabs in workspace
|
2015-04-02 20:43:52 +08:00
|
|
|
if (m_leftTime+m_topTime+m_rightTime+m_bottomTime > 1e-4) {
|
2015-04-01 04:31:45 +08:00
|
|
|
double left = double(m_leftTime) / double(ANI_DROPAREA_TICKS);
|
|
|
|
|
double top = double(m_topTime) / double(ANI_DROPAREA_TICKS);
|
|
|
|
|
double right = double(m_rightTime) / double(ANI_DROPAREA_TICKS);
|
|
|
|
|
double bottom = double(m_bottomTime) / double(ANI_DROPAREA_TICKS);
|
|
|
|
|
double threshold = getDropThreshold();
|
|
|
|
|
|
|
|
|
|
rc.x += int(inbetween(0.0, threshold, left));
|
|
|
|
|
rc.y += int(inbetween(0.0, threshold, top));
|
|
|
|
|
rc.w -= int(inbetween(0.0, threshold, left) + inbetween(0.0, threshold, right));
|
|
|
|
|
rc.h -= int(inbetween(0.0, threshold, top) + inbetween(0.0, threshold, bottom));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (Widget* child : getChildren())
|
|
|
|
|
if (child->isVisible())
|
|
|
|
|
child->setBounds(rc);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-03 02:43:50 +08:00
|
|
|
void WorkspacePanel::setDropViewPreview(const gfx::Point& pos, WorkspaceView* view)
|
2015-04-01 04:31:45 +08:00
|
|
|
{
|
|
|
|
|
int newDropArea = calculateDropArea(pos);
|
|
|
|
|
if (newDropArea != m_dropArea) {
|
|
|
|
|
m_dropArea = newDropArea;
|
|
|
|
|
startAnimation(ANI_DROPAREA, ANI_DROPAREA_TICKS);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorkspacePanel::removeDropViewPreview()
|
|
|
|
|
{
|
|
|
|
|
if (m_dropArea) {
|
|
|
|
|
m_dropArea = 0;
|
|
|
|
|
startAnimation(ANI_DROPAREA, ANI_DROPAREA_TICKS);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-02 20:43:52 +08:00
|
|
|
void WorkspacePanel::onAnimationStop(int animation)
|
2015-04-01 04:31:45 +08:00
|
|
|
{
|
2015-04-02 20:43:52 +08:00
|
|
|
if (animation == ANI_DROPAREA)
|
2015-04-01 04:31:45 +08:00
|
|
|
layout();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorkspacePanel::onAnimationFrame()
|
|
|
|
|
{
|
|
|
|
|
if (animation() == ANI_DROPAREA) {
|
2015-06-24 01:00:00 +08:00
|
|
|
adjustTime(m_leftTime, LEFT);
|
|
|
|
|
adjustTime(m_topTime, TOP);
|
|
|
|
|
adjustTime(m_rightTime, RIGHT);
|
|
|
|
|
adjustTime(m_bottomTime, BOTTOM);
|
2015-04-01 04:31:45 +08:00
|
|
|
layout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorkspacePanel::adjustTime(int& time, int flag)
|
|
|
|
|
{
|
|
|
|
|
if (m_dropArea & flag) {
|
|
|
|
|
if (time < ANI_DROPAREA_TICKS)
|
|
|
|
|
++time;
|
|
|
|
|
}
|
|
|
|
|
else if (time > 0)
|
|
|
|
|
--time;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-21 00:49:25 +08:00
|
|
|
DropViewAtResult WorkspacePanel::dropViewAt(const gfx::Point& pos, WorkspacePanel* from, WorkspaceView* view, bool clone)
|
2015-04-01 04:31:45 +08:00
|
|
|
{
|
|
|
|
|
int dropArea = calculateDropArea(pos);
|
|
|
|
|
if (!dropArea)
|
2015-04-21 00:49:25 +08:00
|
|
|
return DropViewAtResult::NOTHING;
|
2015-04-01 04:31:45 +08:00
|
|
|
|
|
|
|
|
// If we're dropping the view in the same panel, and it's the only
|
|
|
|
|
// view in the panel: We cannot drop the view in the panel (because
|
|
|
|
|
// if we remove the last view, the panel will be destroyed).
|
2015-04-21 00:49:25 +08:00
|
|
|
if (!clone && from == this && m_views.size() == 1)
|
|
|
|
|
return DropViewAtResult::NOTHING;
|
2015-04-01 04:31:45 +08:00
|
|
|
|
|
|
|
|
int splitterAlign = 0;
|
2015-06-24 01:00:00 +08:00
|
|
|
if (dropArea & (LEFT | RIGHT)) splitterAlign = HORIZONTAL;
|
|
|
|
|
else if (dropArea & (TOP | BOTTOM)) splitterAlign = VERTICAL;
|
2015-04-01 04:31:45 +08:00
|
|
|
|
|
|
|
|
ASSERT(from);
|
2015-04-21 00:49:25 +08:00
|
|
|
DropViewAtResult result;
|
|
|
|
|
Workspace* workspace = getWorkspace();
|
2015-04-21 21:24:42 +08:00
|
|
|
WorkspaceView* originalView = view;
|
2015-04-21 00:49:25 +08:00
|
|
|
if (clone) {
|
|
|
|
|
view = view->cloneWorkspaceView();
|
|
|
|
|
result = DropViewAtResult::CLONED_VIEW;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
workspace->removeView(view);
|
|
|
|
|
result = DropViewAtResult::MOVED_TO_OTHER_PANEL;
|
|
|
|
|
}
|
2015-04-01 04:31:45 +08:00
|
|
|
|
|
|
|
|
WorkspaceTabs* newTabs = new WorkspaceTabs(m_tabs->getDelegate());
|
|
|
|
|
WorkspacePanel* newPanel = new WorkspacePanel(SUB_PANEL);
|
2015-04-03 02:43:50 +08:00
|
|
|
newTabs->setDockedStyle();
|
2015-04-01 04:31:45 +08:00
|
|
|
newPanel->setTabsBar(newTabs);
|
|
|
|
|
newPanel->setExpansive(true);
|
|
|
|
|
|
|
|
|
|
Widget* self = this;
|
|
|
|
|
VBox* side = new VBox;
|
|
|
|
|
side->noBorderNoChildSpacing();
|
|
|
|
|
side->addChild(newTabs);
|
|
|
|
|
side->addChild(newPanel);
|
|
|
|
|
|
|
|
|
|
Splitter* splitter = new Splitter(Splitter::ByPercentage, splitterAlign);
|
|
|
|
|
splitter->setExpansive(true);
|
|
|
|
|
|
|
|
|
|
Widget* parent = getParent();
|
2015-06-24 01:37:22 +08:00
|
|
|
if (parent->type() == kBoxWidget) {
|
2015-04-01 04:31:45 +08:00
|
|
|
self = parent;
|
|
|
|
|
parent = self->getParent();
|
2015-06-24 01:37:22 +08:00
|
|
|
ASSERT(parent->type() == kSplitterWidget);
|
2015-04-01 04:31:45 +08:00
|
|
|
}
|
2015-06-24 01:37:22 +08:00
|
|
|
if (parent->type() == Workspace::Type() ||
|
|
|
|
|
parent->type() == kSplitterWidget) {
|
2015-04-01 04:31:45 +08:00
|
|
|
parent->replaceChild(self, splitter);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ASSERT(false);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-03 02:48:38 +08:00
|
|
|
double sideSpace;
|
|
|
|
|
if (m_panelType == MAIN_PANEL)
|
|
|
|
|
sideSpace = 30;
|
|
|
|
|
else
|
|
|
|
|
sideSpace = 50;
|
|
|
|
|
|
2015-04-01 04:31:45 +08:00
|
|
|
switch (dropArea) {
|
2015-06-24 01:00:00 +08:00
|
|
|
case LEFT:
|
|
|
|
|
case TOP:
|
2015-04-03 02:48:38 +08:00
|
|
|
splitter->setPosition(sideSpace);
|
2015-04-01 04:31:45 +08:00
|
|
|
splitter->addChild(side);
|
|
|
|
|
splitter->addChild(self);
|
|
|
|
|
break;
|
2015-06-24 01:00:00 +08:00
|
|
|
case RIGHT:
|
|
|
|
|
case BOTTOM:
|
2015-04-03 02:48:38 +08:00
|
|
|
splitter->setPosition(100-sideSpace);
|
2015-04-01 04:31:45 +08:00
|
|
|
splitter->addChild(self);
|
|
|
|
|
splitter->addChild(side);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-21 00:49:25 +08:00
|
|
|
workspace->addViewToPanel(newPanel, view, true, -1);
|
2015-04-01 04:31:45 +08:00
|
|
|
parent->layout();
|
2015-04-21 21:24:42 +08:00
|
|
|
|
|
|
|
|
if (result == DropViewAtResult::CLONED_VIEW)
|
|
|
|
|
view->onClonedFrom(originalView);
|
|
|
|
|
|
2015-04-21 00:49:25 +08:00
|
|
|
return result;
|
2015-04-01 04:31:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int WorkspacePanel::calculateDropArea(const gfx::Point& pos) const
|
|
|
|
|
{
|
|
|
|
|
gfx::Rect rc = getChildrenBounds();
|
|
|
|
|
if (rc.contains(pos)) {
|
|
|
|
|
int left = ABS(rc.x - pos.x);
|
|
|
|
|
int top = ABS(rc.y - pos.y);
|
|
|
|
|
int right = ABS(rc.x + rc.w - pos.x);
|
|
|
|
|
int bottom = ABS(rc.y + rc.h - pos.y);
|
|
|
|
|
int threshold = getDropThreshold();
|
|
|
|
|
|
|
|
|
|
if (left < threshold && left < right && left < top && left < bottom) {
|
2015-06-24 01:00:00 +08:00
|
|
|
return LEFT;
|
2015-04-01 04:31:45 +08:00
|
|
|
}
|
|
|
|
|
else if (top < threshold && top < left && top < right && top < bottom) {
|
2015-06-24 01:00:00 +08:00
|
|
|
return TOP;
|
2015-04-01 04:31:45 +08:00
|
|
|
}
|
|
|
|
|
else if (right < threshold && right < left && right < top && right < bottom) {
|
2015-06-24 01:00:00 +08:00
|
|
|
return RIGHT;
|
2015-04-01 04:31:45 +08:00
|
|
|
}
|
|
|
|
|
else if (bottom < threshold && bottom < left && bottom < top && bottom < right) {
|
2015-06-24 01:00:00 +08:00
|
|
|
return BOTTOM;
|
2015-04-01 04:31:45 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int WorkspacePanel::getDropThreshold() const
|
|
|
|
|
{
|
|
|
|
|
gfx::Rect cpos = getChildrenBounds();
|
|
|
|
|
int threshold = 32*guiscale();
|
|
|
|
|
if (threshold > cpos.w/2) threshold = cpos.w/2;
|
|
|
|
|
if (threshold > cpos.h/2) threshold = cpos.h/2;
|
|
|
|
|
return threshold;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Workspace* WorkspacePanel::getWorkspace()
|
|
|
|
|
{
|
|
|
|
|
Widget* widget = this;
|
|
|
|
|
while (widget) {
|
2015-06-24 01:37:22 +08:00
|
|
|
if (widget->type() == Workspace::Type())
|
2015-04-01 04:31:45 +08:00
|
|
|
return static_cast<Workspace*>(widget);
|
|
|
|
|
|
|
|
|
|
widget = widget->getParent();
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace app
|