mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-17 13:20:45 +00:00
Add support to dock tabs as other Workspace panels
This commit is contained in:
parent
6d107734f8
commit
86afa3a568
@ -344,6 +344,8 @@ add_library(app-lib
|
||||
ui/timeline.cpp
|
||||
ui/toolbar.cpp
|
||||
ui/workspace.cpp
|
||||
ui/workspace_panel.cpp
|
||||
ui/workspace_tabs.cpp
|
||||
ui_context.cpp
|
||||
util/autocrop.cpp
|
||||
util/boundary.cpp
|
||||
|
@ -183,6 +183,7 @@ Editor::Editor(Document* document, EditorFlags flags)
|
||||
|
||||
Editor::~Editor()
|
||||
{
|
||||
m_observers.notifyDestroyEditor(this);
|
||||
m_document->removeObserver(this);
|
||||
|
||||
setCustomizationDelegate(NULL);
|
||||
|
@ -18,6 +18,8 @@ namespace app {
|
||||
virtual ~EditorObserver() { }
|
||||
virtual void dispose() { }
|
||||
|
||||
virtual void onDestroyEditor(Editor* editor) { }
|
||||
|
||||
// Called when the editor's state changes.
|
||||
virtual void onStateChanged(Editor* editor) { }
|
||||
|
||||
|
@ -30,6 +30,11 @@ void EditorObservers::removeObserver(EditorObserver* observer)
|
||||
m_observers.removeObserver(observer);
|
||||
}
|
||||
|
||||
void EditorObservers::notifyDestroyEditor(Editor* editor)
|
||||
{
|
||||
m_observers.notifyObservers(&EditorObserver::onDestroyEditor, editor);
|
||||
}
|
||||
|
||||
void EditorObservers::notifyStateChanged(Editor* editor)
|
||||
{
|
||||
m_observers.notifyObservers(&EditorObserver::onStateChanged, editor);
|
||||
|
@ -22,6 +22,7 @@ namespace app {
|
||||
void addObserver(EditorObserver* observer);
|
||||
void removeObserver(EditorObserver* observer);
|
||||
|
||||
void notifyDestroyEditor(Editor* editor);
|
||||
void notifyStateChanged(Editor* editor);
|
||||
void notifyScrollChanged(Editor* editor);
|
||||
void notifyBeforeFrameChanged(Editor* editor);
|
||||
|
@ -302,11 +302,19 @@ void MainWindow::onFloatingTab(Tabs* tabs, TabView* tabView, const gfx::Point& p
|
||||
m_workspace->setDropViewPreview(pos);
|
||||
}
|
||||
|
||||
void MainWindow::onDockingTab(Tabs* tabs, TabView* tabView)
|
||||
{
|
||||
m_workspace->removeDropViewPreview();
|
||||
}
|
||||
|
||||
DropTabResult MainWindow::onDropTab(Tabs* tabs, TabView* tabView, const gfx::Point& pos)
|
||||
{
|
||||
m_workspace->removeDropViewPreview(pos);
|
||||
m_workspace->dropViewAt(pos, dynamic_cast<WorkspaceView*>(tabView));
|
||||
return DropTabResult::IGNORE;
|
||||
m_workspace->removeDropViewPreview();
|
||||
|
||||
if (m_workspace->dropViewAt(pos, dynamic_cast<WorkspaceView*>(tabView)))
|
||||
return DropTabResult::DOCKED_IN_OTHER_PLACE;
|
||||
else
|
||||
return DropTabResult::IGNORE;
|
||||
}
|
||||
|
||||
void MainWindow::configureWorkspaceLayout()
|
||||
@ -335,6 +343,7 @@ void MainWindow::configureWorkspaceLayout()
|
||||
}
|
||||
|
||||
layout();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -80,6 +80,7 @@ namespace app {
|
||||
void onContextMenuTab(Tabs* tabs, TabView* tabView) override;
|
||||
void onMouseOverTab(Tabs* tabs, TabView* tabView) override;
|
||||
void onFloatingTab(Tabs* tabs, TabView* tabView, const gfx::Point& pos) override;
|
||||
void onDockingTab(Tabs* tabs, TabView* tabView) override;
|
||||
DropTabResult onDropTab(Tabs* tabs, TabView* tabView, const gfx::Point& pos) override;
|
||||
|
||||
protected:
|
||||
|
@ -63,17 +63,12 @@ Tabs::Tabs(TabsDelegate* delegate)
|
||||
|
||||
Tabs::~Tabs()
|
||||
{
|
||||
if (m_removedTab) {
|
||||
delete m_removedTab;
|
||||
m_removedTab = nullptr;
|
||||
}
|
||||
m_removedTab = nullptr;
|
||||
|
||||
// Stop animation
|
||||
stopAnimation();
|
||||
|
||||
// Remove all tabs
|
||||
for (Tab* tab : m_list)
|
||||
delete tab;
|
||||
m_list.clear();
|
||||
}
|
||||
|
||||
@ -82,7 +77,7 @@ void Tabs::addTab(TabView* tabView, int pos)
|
||||
resetOldPositions();
|
||||
startAnimation(ANI_ADDING_TAB, ANI_ADDING_TAB_TICKS);
|
||||
|
||||
Tab* tab = new Tab(tabView);
|
||||
TabPtr tab(new Tab(tabView));
|
||||
if (pos < 0)
|
||||
m_list.push_back(tab);
|
||||
else
|
||||
@ -94,9 +89,9 @@ void Tabs::addTab(TabView* tabView, int pos)
|
||||
tab->modified = false;
|
||||
}
|
||||
|
||||
void Tabs::removeTab(TabView* tabView)
|
||||
void Tabs::removeTab(TabView* tabView, bool with_animation)
|
||||
{
|
||||
Tab* tab = getTabByView(tabView);
|
||||
TabPtr tab(getTabByView(tabView));
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
@ -118,15 +113,17 @@ void Tabs::removeTab(TabView* tabView)
|
||||
ASSERT(it != m_list.end() && "Removing a tab that is not part of the Tabs widget");
|
||||
it = m_list.erase(it);
|
||||
|
||||
delete m_removedTab;
|
||||
m_removedTab = tab;
|
||||
|
||||
if (m_delegate)
|
||||
tab->modified = m_delegate->onIsModified(this, tabView);
|
||||
tab->view = nullptr; // The view will be destroyed after Tabs::removeTab() anyway
|
||||
if (with_animation) {
|
||||
if (m_delegate)
|
||||
tab->modified = m_delegate->onIsModified(this, tabView);
|
||||
tab->view = nullptr; // The view will be destroyed after Tabs::removeTab() anyway
|
||||
|
||||
resetOldPositions();
|
||||
startAnimation(ANI_REMOVING_TAB, ANI_REMOVING_TAB_TICKS);
|
||||
}
|
||||
|
||||
resetOldPositions();
|
||||
startAnimation(ANI_REMOVING_TAB, ANI_REMOVING_TAB_TICKS);
|
||||
updateTabs();
|
||||
}
|
||||
|
||||
@ -142,7 +139,7 @@ void Tabs::updateTabs()
|
||||
}
|
||||
double x = 0.0;
|
||||
|
||||
for (Tab* tab : m_list) {
|
||||
for (auto& tab : m_list) {
|
||||
if (tab == m_floatingTab)
|
||||
continue;
|
||||
|
||||
@ -159,7 +156,7 @@ void Tabs::selectTab(TabView* tabView)
|
||||
{
|
||||
ASSERT(tabView != NULL);
|
||||
|
||||
Tab* tab = getTabByView(tabView);
|
||||
TabPtr tab(getTabByView(tabView));
|
||||
if (tab)
|
||||
selectTabInternal(tab);
|
||||
}
|
||||
@ -231,10 +228,13 @@ bool Tabs::onProcessMessage(Message* msg)
|
||||
}
|
||||
// We are drag a tab...
|
||||
else {
|
||||
Tab* justDocked = nullptr;
|
||||
TabPtr justDocked(nullptr);
|
||||
|
||||
// Floating tab (to create a new window)
|
||||
if (ABS(delta.y) > 16*guiscale()) {
|
||||
if (!getBounds().contains(mousePos) &&
|
||||
(ABS(delta.y) > 16*guiscale() ||
|
||||
mousePos.x < getBounds().x-16*guiscale() ||
|
||||
mousePos.x > getBounds().x2()+16*guiscale())) {
|
||||
if (!m_floatingOverlay)
|
||||
createFloatingTab(m_selected);
|
||||
|
||||
@ -246,6 +246,9 @@ bool Tabs::onProcessMessage(Message* msg)
|
||||
else {
|
||||
justDocked = m_floatingTab;
|
||||
destroyFloatingTab();
|
||||
|
||||
if (m_delegate)
|
||||
m_delegate->onDockingTab(this, m_selected->view);
|
||||
}
|
||||
|
||||
// Docked tab
|
||||
@ -285,7 +288,7 @@ bool Tabs::onProcessMessage(Message* msg)
|
||||
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
||||
m_dragMousePos = mouseMsg->position();
|
||||
m_dragOffset = mouseMsg->position() -
|
||||
(getBounds().getOrigin() + getTabBounds(m_hot).getOrigin());
|
||||
(getBounds().getOrigin() + getTabBounds(m_hot.get()).getOrigin());
|
||||
|
||||
if (m_hotCloseButton) {
|
||||
if (!m_clickedCloseButton) {
|
||||
@ -325,11 +328,15 @@ bool Tabs::onProcessMessage(Message* msg)
|
||||
releaseMouse();
|
||||
|
||||
if (m_isDragging) {
|
||||
if (m_delegate)
|
||||
m_delegate->onDropTab(this, m_selected->view,
|
||||
mouseMsg->position());
|
||||
DropTabResult result = DropTabResult::IGNORE;
|
||||
|
||||
stopDrag();
|
||||
if (m_delegate) {
|
||||
result =
|
||||
m_delegate->onDropTab(this, m_selected->view,
|
||||
mouseMsg->position());
|
||||
}
|
||||
|
||||
stopDrag(result);
|
||||
}
|
||||
|
||||
if (m_clickedCloseButton) {
|
||||
@ -374,14 +381,14 @@ void Tabs::onPaint(PaintEvent& ev)
|
||||
drawFiller(g, box);
|
||||
|
||||
// For each tab...
|
||||
for (Tab* tab : m_list) {
|
||||
for (TabPtr& tab : m_list) {
|
||||
if (tab == m_floatingTab)
|
||||
continue;
|
||||
|
||||
box = getTabBounds(tab);
|
||||
box = getTabBounds(tab.get());
|
||||
|
||||
if (tab != m_selected)
|
||||
drawTab(g, box, tab, 0, (tab == m_hot), false);
|
||||
drawTab(g, box, tab.get(), 0, (tab == m_hot), false);
|
||||
|
||||
box.x = box.x2();
|
||||
}
|
||||
@ -389,8 +396,8 @@ void Tabs::onPaint(PaintEvent& ev)
|
||||
// Draw deleted tab
|
||||
if (animation() == ANI_REMOVING_TAB && m_removedTab) {
|
||||
m_removedTab->width = 0;
|
||||
box = getTabBounds(m_removedTab);
|
||||
drawTab(g, box, m_removedTab, 0,
|
||||
box = getTabBounds(m_removedTab.get());
|
||||
drawTab(g, box, m_removedTab.get(), 0,
|
||||
(m_removedTab == m_floatingTab),
|
||||
(m_removedTab == m_floatingTab));
|
||||
}
|
||||
@ -398,14 +405,14 @@ void Tabs::onPaint(PaintEvent& ev)
|
||||
// Tab that is being dragged
|
||||
if (m_selected && m_selected != m_floatingTab) {
|
||||
double t = animationTime();
|
||||
Tab* tab = m_selected;
|
||||
box = getTabBounds(tab);
|
||||
TabPtr tab(m_selected);
|
||||
box = getTabBounds(tab.get());
|
||||
|
||||
int dy = 0;
|
||||
if (animation() == ANI_ADDING_TAB)
|
||||
dy = int(box.h - box.h * t);
|
||||
|
||||
drawTab(g, box, m_selected, dy, (tab == m_hot), true);
|
||||
drawTab(g, box, m_selected.get(), dy, (tab == m_hot), true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -428,10 +435,10 @@ void Tabs::onPreferredSize(PreferredSizeEvent& ev)
|
||||
ev.setPreferredSize(reqsize);
|
||||
}
|
||||
|
||||
void Tabs::selectTabInternal(Tab* tab)
|
||||
void Tabs::selectTabInternal(TabPtr& tab)
|
||||
{
|
||||
m_selected = tab;
|
||||
makeTabVisible(tab);
|
||||
makeTabVisible(tab.get());
|
||||
invalidate();
|
||||
|
||||
if (m_delegate)
|
||||
@ -555,13 +562,13 @@ Tabs::TabsListIterator Tabs::getTabIteratorByView(TabView* tabView)
|
||||
return it;
|
||||
}
|
||||
|
||||
Tabs::Tab* Tabs::getTabByView(TabView* tabView)
|
||||
Tabs::TabPtr Tabs::getTabByView(TabView* tabView)
|
||||
{
|
||||
TabsListIterator it = getTabIteratorByView(tabView);
|
||||
if (it != m_list.end())
|
||||
return *it;
|
||||
return TabPtr(*it);
|
||||
else
|
||||
return NULL;
|
||||
return TabPtr(nullptr);
|
||||
}
|
||||
|
||||
void Tabs::makeTabVisible(Tab* thisTab)
|
||||
@ -577,11 +584,11 @@ void Tabs::calculateHot()
|
||||
gfx::Rect rect = getBounds();
|
||||
gfx::Rect box(rect.x+m_border*guiscale(), rect.y, 0, rect.h-1);
|
||||
gfx::Point mousePos = ui::get_mouse_position();
|
||||
Tab* hot = NULL;
|
||||
TabPtr hot(nullptr);
|
||||
bool hotCloseButton = false;
|
||||
|
||||
// For each tab
|
||||
for (Tab* tab : m_list) {
|
||||
for (TabPtr& tab : m_list) {
|
||||
if (tab == m_floatingTab)
|
||||
continue;
|
||||
|
||||
@ -589,7 +596,7 @@ void Tabs::calculateHot()
|
||||
|
||||
if (box.contains(mousePos)) {
|
||||
hot = tab;
|
||||
hotCloseButton = getTabCloseButtonBounds(tab, box).contains(mousePos);
|
||||
hotCloseButton = getTabCloseButtonBounds(tab.get(), box).contains(mousePos);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -614,7 +621,7 @@ gfx::Rect Tabs::getTabCloseButtonBounds(Tab* tab, const gfx::Rect& box)
|
||||
int iconW = theme->dimensions.tabsCloseIconWidth();
|
||||
int iconH = theme->dimensions.tabsCloseIconHeight();
|
||||
|
||||
if (box.w-iconW > 32*ui::guiscale() || tab == m_selected)
|
||||
if (box.w-iconW > 32*ui::guiscale() || tab == m_selected.get())
|
||||
return gfx::Rect(box.x2()-iconW, box.y+box.h/2-iconH/2, iconW, iconH);
|
||||
else
|
||||
return gfx::Rect();
|
||||
@ -622,7 +629,7 @@ gfx::Rect Tabs::getTabCloseButtonBounds(Tab* tab, const gfx::Rect& box)
|
||||
|
||||
void Tabs::resetOldPositions()
|
||||
{
|
||||
for (Tab* tab : m_list) {
|
||||
for (TabPtr& tab : m_list) {
|
||||
if (tab == m_floatingTab)
|
||||
continue;
|
||||
|
||||
@ -633,7 +640,7 @@ void Tabs::resetOldPositions()
|
||||
|
||||
void Tabs::resetOldPositions(double t)
|
||||
{
|
||||
for (Tab* tab : m_list) {
|
||||
for (TabPtr& tab : m_list) {
|
||||
if (tab == m_floatingTab)
|
||||
continue;
|
||||
|
||||
@ -667,20 +674,39 @@ void Tabs::startDrag()
|
||||
m_dragTabIndex = std::find(m_list.begin(), m_list.end(), m_selected) - m_list.begin();
|
||||
}
|
||||
|
||||
void Tabs::stopDrag()
|
||||
void Tabs::stopDrag(DropTabResult result)
|
||||
{
|
||||
m_isDragging = false;
|
||||
|
||||
destroyFloatingTab();
|
||||
switch (result) {
|
||||
|
||||
case DropTabResult::IGNORE:
|
||||
destroyFloatingTab();
|
||||
|
||||
ASSERT(m_selected);
|
||||
if (m_selected) {
|
||||
m_selected->oldX = m_selected->x;
|
||||
m_selected->oldWidth = m_selected->width;
|
||||
}
|
||||
resetOldPositions(animationTime());
|
||||
updateTabs();
|
||||
startAnimation(ANI_REORDER_TABS, ANI_REORDER_TABS_TICKS);
|
||||
break;
|
||||
|
||||
case DropTabResult::DOCKED_IN_OTHER_PLACE: {
|
||||
TabPtr tab = m_floatingTab;
|
||||
|
||||
m_floatingTab = nullptr;
|
||||
m_removedTab = nullptr;
|
||||
destroyFloatingTab();
|
||||
|
||||
ASSERT(tab);
|
||||
if (tab)
|
||||
removeTab(tab->view, false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_selected) {
|
||||
m_selected->oldX = m_selected->x;
|
||||
m_selected->oldWidth = m_selected->width;
|
||||
}
|
||||
|
||||
resetOldPositions(animationTime());
|
||||
updateTabs();
|
||||
startAnimation(ANI_REORDER_TABS, ANI_REORDER_TABS_TICKS);
|
||||
}
|
||||
|
||||
gfx::Rect Tabs::getTabBounds(Tab* tab)
|
||||
@ -705,7 +731,7 @@ gfx::Rect Tabs::getTabBounds(Tab* tab)
|
||||
return box;
|
||||
}
|
||||
|
||||
void Tabs::createFloatingTab(Tab* tab)
|
||||
void Tabs::createFloatingTab(TabPtr& tab)
|
||||
{
|
||||
ASSERT(!m_floatingOverlay);
|
||||
|
||||
@ -717,7 +743,7 @@ void Tabs::createFloatingTab(Tab* tab)
|
||||
Graphics g(surface, 0, 0);
|
||||
g.setFont(getFont());
|
||||
g.fillRect(gfx::ColorNone, g.getClipBounds());
|
||||
drawTab(&g, g.getClipBounds(), tab, 0, true, true);
|
||||
drawTab(&g, g.getClipBounds(), tab.get(), 0, true, true);
|
||||
}
|
||||
|
||||
// Make opaque (TODO this shouldn't be necessary)
|
||||
@ -733,8 +759,8 @@ void Tabs::createFloatingTab(Tab* tab)
|
||||
}
|
||||
}
|
||||
|
||||
m_floatingOverlay = new Overlay(surface, gfx::Point(), Overlay::MouseZOrder-1);
|
||||
OverlayManager::instance()->addOverlay(m_floatingOverlay);
|
||||
m_floatingOverlay.reset(new Overlay(surface, gfx::Point(), Overlay::MouseZOrder-1));
|
||||
OverlayManager::instance()->addOverlay(m_floatingOverlay.get());
|
||||
|
||||
resetOldPositions();
|
||||
|
||||
@ -747,13 +773,12 @@ void Tabs::createFloatingTab(Tab* tab)
|
||||
void Tabs::destroyFloatingTab()
|
||||
{
|
||||
if (m_floatingOverlay) {
|
||||
OverlayManager::instance()->removeOverlay(m_floatingOverlay);
|
||||
delete m_floatingOverlay;
|
||||
OverlayManager::instance()->removeOverlay(m_floatingOverlay.get());
|
||||
m_floatingOverlay = nullptr;
|
||||
}
|
||||
|
||||
if (m_floatingTab) {
|
||||
Tab* tab = m_floatingTab;
|
||||
TabPtr tab(m_floatingTab);
|
||||
m_floatingTab = nullptr;
|
||||
|
||||
resetOldPositions();
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "ui/widget.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace ui {
|
||||
class Graphics;
|
||||
@ -71,6 +72,7 @@ namespace app {
|
||||
|
||||
// Called when the user is dragging a tab outside the Tabs bar.
|
||||
virtual void onFloatingTab(Tabs* tabs, TabView* tabView, const gfx::Point& pos) = 0;
|
||||
virtual void onDockingTab(Tabs* tabs, TabView* tabView) = 0;
|
||||
virtual DropTabResult onDropTab(Tabs* tabs, TabView* tabView, const gfx::Point& pos) = 0;
|
||||
};
|
||||
|
||||
@ -89,7 +91,9 @@ namespace app {
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<Tab*> TabsList;
|
||||
typedef std::shared_ptr<Tab> TabPtr;
|
||||
|
||||
typedef std::vector<TabPtr> TabsList;
|
||||
typedef TabsList::iterator TabsListIterator;
|
||||
|
||||
enum Ani : int {
|
||||
@ -103,8 +107,10 @@ namespace app {
|
||||
Tabs(TabsDelegate* delegate);
|
||||
~Tabs();
|
||||
|
||||
TabsDelegate* getDelegate() { return m_delegate; }
|
||||
|
||||
void addTab(TabView* tabView, int pos = -1);
|
||||
void removeTab(TabView* tabView);
|
||||
void removeTab(TabView* tabView, bool with_animation);
|
||||
void updateTabs();
|
||||
|
||||
void selectTab(TabView* tabView);
|
||||
@ -124,33 +130,33 @@ namespace app {
|
||||
void resetOldPositions();
|
||||
void resetOldPositions(double t);
|
||||
|
||||
void selectTabInternal(Tab* tab);
|
||||
void selectTabInternal(TabPtr& tab);
|
||||
void drawTab(ui::Graphics* g, const gfx::Rect& box, Tab* tab, int dy, bool hover, bool selected);
|
||||
void drawFiller(ui::Graphics* g, const gfx::Rect& box);
|
||||
TabsListIterator getTabIteratorByView(TabView* tabView);
|
||||
Tab* getTabByView(TabView* tabView);
|
||||
TabPtr getTabByView(TabView* tabView);
|
||||
int getMaxScrollX();
|
||||
void makeTabVisible(Tab* tab);
|
||||
void calculateHot();
|
||||
gfx::Rect getTabCloseButtonBounds(Tab* tab, const gfx::Rect& box);
|
||||
void startDrag();
|
||||
void stopDrag();
|
||||
void stopDrag(DropTabResult result);
|
||||
gfx::Rect getTabBounds(Tab* tab);
|
||||
void createFloatingTab(Tab* tab);
|
||||
void createFloatingTab(TabPtr& tab);
|
||||
void destroyFloatingTab();
|
||||
|
||||
int m_border;
|
||||
TabsList m_list;
|
||||
Tab* m_hot;
|
||||
TabPtr m_hot;
|
||||
bool m_hotCloseButton;
|
||||
bool m_clickedCloseButton;
|
||||
Tab* m_selected;
|
||||
TabPtr m_selected;
|
||||
|
||||
// Delegate of notifications
|
||||
TabsDelegate* m_delegate;
|
||||
|
||||
// Variables for animation purposes
|
||||
Tab* m_removedTab;
|
||||
TabPtr m_removedTab;
|
||||
|
||||
// Drag-and-drop
|
||||
bool m_isDragging;
|
||||
@ -158,8 +164,8 @@ namespace app {
|
||||
gfx::Point m_dragMousePos;
|
||||
gfx::Point m_dragOffset;
|
||||
int m_dragTabIndex;
|
||||
Tab* m_floatingTab;
|
||||
ui::Overlay* m_floatingOverlay;
|
||||
TabPtr m_floatingTab;
|
||||
std::unique_ptr<ui::Overlay> m_floatingOverlay;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -988,6 +988,15 @@ void Timeline::onAfterLayerChanged(Editor* editor)
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void Timeline::onDestroyEditor(Editor* editor)
|
||||
{
|
||||
ASSERT(m_editor == editor);
|
||||
if (m_editor == editor) {
|
||||
m_editor->removeObserver(this);
|
||||
m_editor = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Timeline::setCursor(ui::Message* msg, const Hit& hit)
|
||||
{
|
||||
// Scrolling.
|
||||
|
@ -114,6 +114,7 @@ namespace app {
|
||||
// EditorObserver impl.
|
||||
void onAfterFrameChanged(Editor* editor) override;
|
||||
void onAfterLayerChanged(Editor* editor) override;
|
||||
void onDestroyEditor(Editor* editor) override;
|
||||
|
||||
private:
|
||||
struct Hit {
|
||||
|
@ -11,35 +11,38 @@
|
||||
|
||||
#include "app/ui/workspace.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/ui/main_window.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/ui/tabs.h"
|
||||
#include "app/ui/workspace_view.h"
|
||||
#include "base/remove_from_container.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
#include "ui/paint_event.h"
|
||||
#include "ui/resize_event.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
#define ANI_DROPAREA_TICKS 4
|
||||
|
||||
using namespace app::skin;
|
||||
using namespace ui;
|
||||
|
||||
// static
|
||||
WidgetType Workspace::Type()
|
||||
{
|
||||
static WidgetType type = kGenericWidget;
|
||||
if (type == kGenericWidget)
|
||||
type = register_widget_type();
|
||||
return type;
|
||||
}
|
||||
|
||||
Workspace::Workspace()
|
||||
: Widget(kGenericWidget)
|
||||
, m_tabsBar(nullptr)
|
||||
, m_activeView(nullptr)
|
||||
, m_dropArea(0)
|
||||
, m_leftTime(0)
|
||||
, m_rightTime(0)
|
||||
, m_topTime(0)
|
||||
, m_bottomTime(0)
|
||||
: Widget(Workspace::Type())
|
||||
, m_mainPanel(WorkspacePanel::MAIN_PANEL)
|
||||
, m_tabs(nullptr)
|
||||
, m_activePanel(&m_mainPanel)
|
||||
, m_dropPreviewPanel(nullptr)
|
||||
{
|
||||
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
|
||||
setBgColor(theme->colors.workspace());
|
||||
|
||||
addChild(&m_mainPanel);
|
||||
}
|
||||
|
||||
Workspace::~Workspace()
|
||||
@ -48,19 +51,18 @@ Workspace::~Workspace()
|
||||
ASSERT(m_views.empty());
|
||||
}
|
||||
|
||||
void Workspace::setTabsBar(Tabs* tabsBar)
|
||||
void Workspace::setTabsBar(Tabs* tabs)
|
||||
{
|
||||
m_tabsBar = tabsBar;
|
||||
m_tabs = tabs;
|
||||
m_mainPanel.setTabsBar(tabs);
|
||||
}
|
||||
|
||||
void Workspace::addView(WorkspaceView* view, int pos)
|
||||
{
|
||||
if (pos < 0)
|
||||
m_views.push_back(view);
|
||||
else
|
||||
m_views.insert(m_views.begin()+pos, view);
|
||||
m_mainPanel.addView(view, pos);
|
||||
m_activePanel = &m_mainPanel;
|
||||
m_views.push_back(view);
|
||||
|
||||
m_tabsBar->addTab(dynamic_cast<TabView*>(view), pos);
|
||||
setActiveView(view);
|
||||
}
|
||||
|
||||
@ -68,15 +70,10 @@ void Workspace::removeView(WorkspaceView* view)
|
||||
{
|
||||
base::remove_from_container(m_views, view);
|
||||
|
||||
Widget* content = view->getContentWidget();
|
||||
if (content->getParent())
|
||||
content->getParent()->removeChild(content);
|
||||
|
||||
// Remove related tab.
|
||||
m_tabsBar->removeTab(dynamic_cast<TabView*>(view));
|
||||
|
||||
TabView* tabView = m_tabsBar->getSelectedTab();
|
||||
setActiveView(dynamic_cast<WorkspaceView*>(tabView));
|
||||
WorkspacePanel* panel = getViewPanel(view);
|
||||
ASSERT(panel);
|
||||
if (panel)
|
||||
panel->removeView(view);
|
||||
}
|
||||
|
||||
bool Workspace::closeView(WorkspaceView* view)
|
||||
@ -86,19 +83,24 @@ bool Workspace::closeView(WorkspaceView* view)
|
||||
|
||||
WorkspaceView* Workspace::activeView()
|
||||
{
|
||||
return m_activeView;
|
||||
return (m_activePanel ? m_activePanel->activeView(): nullptr);
|
||||
}
|
||||
|
||||
void Workspace::setActiveView(WorkspaceView* view)
|
||||
{
|
||||
m_activeView = view;
|
||||
m_activePanel = getViewPanel(view);
|
||||
ASSERT(m_activePanel);
|
||||
if (!m_activePanel)
|
||||
return;
|
||||
|
||||
removeAllChildren();
|
||||
if (view)
|
||||
addChild(view->getContentWidget());
|
||||
m_activePanel->setActiveView(view);
|
||||
|
||||
layout();
|
||||
ActiveViewChanged(); // Fire ActiveViewChanged event
|
||||
}
|
||||
|
||||
void Workspace::setMainPanelAsActive()
|
||||
{
|
||||
m_activePanel = &m_mainPanel;
|
||||
ActiveViewChanged(); // Fire ActiveViewChanged event
|
||||
}
|
||||
|
||||
@ -112,107 +114,59 @@ void Workspace::onResize(ui::ResizeEvent& ev)
|
||||
setBoundsQuietly(ev.getBounds());
|
||||
|
||||
gfx::Rect rc = getChildrenBounds();
|
||||
|
||||
// Preview to drop tabs in workspace
|
||||
if (animation() == ANI_DROPAREA) {
|
||||
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())
|
||||
child->setBounds(rc);
|
||||
}
|
||||
|
||||
void Workspace::setDropViewPreview(const gfx::Point& pos)
|
||||
{
|
||||
int newDropArea = calculateDropArea(pos);
|
||||
if (newDropArea != m_dropArea) {
|
||||
m_dropArea = newDropArea;
|
||||
startAnimation(ANI_DROPAREA, ANI_DROPAREA_TICKS);
|
||||
WorkspacePanel* panel = getPanelAt(pos);
|
||||
|
||||
if (m_dropPreviewPanel && m_dropPreviewPanel != panel)
|
||||
m_dropPreviewPanel->removeDropViewPreview();
|
||||
|
||||
m_dropPreviewPanel = panel;
|
||||
|
||||
if (m_dropPreviewPanel)
|
||||
m_dropPreviewPanel->setDropViewPreview(pos);
|
||||
}
|
||||
|
||||
void Workspace::removeDropViewPreview()
|
||||
{
|
||||
if (m_dropPreviewPanel)
|
||||
m_dropPreviewPanel->removeDropViewPreview();
|
||||
}
|
||||
|
||||
bool Workspace::dropViewAt(const gfx::Point& pos, WorkspaceView* view)
|
||||
{
|
||||
if (!m_dropPreviewPanel)
|
||||
return false;
|
||||
|
||||
return m_dropPreviewPanel->dropViewAt(pos, getViewPanel(view), view);
|
||||
}
|
||||
|
||||
WorkspacePanel* Workspace::getViewPanel(WorkspaceView* view)
|
||||
{
|
||||
Widget* widget = view->getContentWidget();
|
||||
while (widget) {
|
||||
if (widget->getType() == WorkspacePanel::Type())
|
||||
return static_cast<WorkspacePanel*>(widget);
|
||||
|
||||
widget = widget->getParent();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Workspace::removeDropViewPreview(const gfx::Point& pos)
|
||||
WorkspacePanel* Workspace::getPanelAt(const gfx::Point& pos)
|
||||
{
|
||||
if (m_dropArea) {
|
||||
m_dropArea = 0;
|
||||
startAnimation(ANI_DROPAREA, ANI_DROPAREA_TICKS);
|
||||
Widget* widget = getManager()->pick(pos);
|
||||
while (widget) {
|
||||
if (widget->getType() == WorkspacePanel::Type())
|
||||
return static_cast<WorkspacePanel*>(widget);
|
||||
|
||||
widget = widget->getParent();
|
||||
}
|
||||
}
|
||||
|
||||
void Workspace::onAnimationStop()
|
||||
{
|
||||
if (animation() == ANI_DROPAREA)
|
||||
layout();
|
||||
}
|
||||
|
||||
void Workspace::onAnimationFrame()
|
||||
{
|
||||
if (animation() == ANI_DROPAREA) {
|
||||
adjustTime(m_leftTime, JI_LEFT);
|
||||
adjustTime(m_topTime, JI_TOP);
|
||||
adjustTime(m_rightTime, JI_RIGHT);
|
||||
adjustTime(m_bottomTime, JI_BOTTOM);
|
||||
layout();
|
||||
}
|
||||
}
|
||||
|
||||
void Workspace::adjustTime(int& time, int flag)
|
||||
{
|
||||
if (m_dropArea & flag) {
|
||||
if (time < ANI_DROPAREA_TICKS)
|
||||
++time;
|
||||
}
|
||||
else if (time > 0)
|
||||
--time;
|
||||
}
|
||||
|
||||
void Workspace::dropViewAt(const gfx::Point& pos, WorkspaceView* view)
|
||||
{
|
||||
}
|
||||
|
||||
int Workspace::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) {
|
||||
return JI_LEFT;
|
||||
}
|
||||
else if (top < threshold && top < left && top < right && top < bottom) {
|
||||
return JI_TOP;
|
||||
}
|
||||
else if (right < threshold && right < left && right < top && right < bottom) {
|
||||
return JI_RIGHT;
|
||||
}
|
||||
else if (bottom < threshold && bottom < left && bottom < top && bottom < right) {
|
||||
return JI_BOTTOM;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Workspace::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;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -9,31 +9,24 @@
|
||||
#define APP_UI_WORKSPACE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/ui/animated_widget.h"
|
||||
#include "app/ui/workspace_views.h"
|
||||
#include "app/ui/workspace_panel.h"
|
||||
#include "base/signal.h"
|
||||
#include "ui/widget.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace app {
|
||||
class Tabs;
|
||||
|
||||
class Workspace : public ui::Widget
|
||||
, public AnimatedWidget {
|
||||
enum Ani : int {
|
||||
ANI_NONE,
|
||||
ANI_DROPAREA,
|
||||
};
|
||||
|
||||
class Workspace : public ui::Widget {
|
||||
public:
|
||||
typedef WorkspaceViews::iterator iterator;
|
||||
|
||||
static ui::WidgetType Type();
|
||||
|
||||
Workspace();
|
||||
~Workspace();
|
||||
|
||||
void setTabsBar(Tabs* tabsBar);
|
||||
void setTabsBar(Tabs* tabs);
|
||||
|
||||
iterator begin() { return m_views.begin(); }
|
||||
iterator end() { return m_views.end(); }
|
||||
|
||||
@ -46,31 +39,33 @@ namespace app {
|
||||
|
||||
WorkspaceView* activeView();
|
||||
void setActiveView(WorkspaceView* view);
|
||||
void setMainPanelAsActive();
|
||||
|
||||
// Drop views into workspace
|
||||
void setDropViewPreview(const gfx::Point& pos);
|
||||
void removeDropViewPreview(const gfx::Point& pos);
|
||||
void dropViewAt(const gfx::Point& pos, WorkspaceView* view);
|
||||
void removeDropViewPreview();
|
||||
|
||||
// Returns true if the view was docked inside the workspace.
|
||||
bool dropViewAt(const gfx::Point& pos, WorkspaceView* view);
|
||||
|
||||
Signal0<void> ActiveViewChanged;
|
||||
|
||||
protected:
|
||||
void onPaint(ui::PaintEvent& ev) override;
|
||||
void onResize(ui::ResizeEvent& ev) override;
|
||||
void onAnimationFrame() override;
|
||||
void onAnimationStop() override;
|
||||
|
||||
private:
|
||||
int calculateDropArea(const gfx::Point& pos) const;
|
||||
int getDropThreshold() const;
|
||||
void adjustTime(int& time, int flag);
|
||||
WorkspacePanel* getViewPanel(WorkspaceView* view);
|
||||
WorkspacePanel* getPanelAt(const gfx::Point& pos);
|
||||
|
||||
Tabs* m_tabsBar;
|
||||
WorkspacePanel m_mainPanel;
|
||||
Tabs* m_tabs;
|
||||
WorkspaceViews m_views;
|
||||
WorkspaceView* m_activeView;
|
||||
int m_dropArea;
|
||||
int m_leftTime, m_rightTime;
|
||||
int m_topTime, m_bottomTime;
|
||||
WorkspacePanel* m_activePanel;
|
||||
WorkspacePanel* m_dropPreviewPanel;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
343
src/app/ui/workspace_panel.cpp
Normal file
343
src/app/ui/workspace_panel.cpp
Normal file
@ -0,0 +1,343 @@
|
||||
// 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/tabs.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());
|
||||
}
|
||||
|
||||
void WorkspacePanel::setTabsBar(Tabs* tabs)
|
||||
{
|
||||
m_tabs = tabs;
|
||||
}
|
||||
|
||||
void WorkspacePanel::addView(WorkspaceView* view, int pos)
|
||||
{
|
||||
if (pos < 0)
|
||||
m_views.push_back(view);
|
||||
else
|
||||
m_views.insert(m_views.begin()+pos, view);
|
||||
|
||||
if (m_tabs)
|
||||
m_tabs->addTab(dynamic_cast<TabView*>(view), pos);
|
||||
|
||||
// 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();
|
||||
ASSERT(self->getType() == kBoxWidget);
|
||||
|
||||
Widget* splitter = self->getParent();
|
||||
ASSERT(splitter->getType() == kSplitterWidget);
|
||||
|
||||
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
|
||||
if (animation() == ANI_DROPAREA) {
|
||||
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);
|
||||
}
|
||||
|
||||
void WorkspacePanel::setDropViewPreview(const gfx::Point& pos)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void WorkspacePanel::onAnimationStop()
|
||||
{
|
||||
if (animation() == ANI_DROPAREA)
|
||||
layout();
|
||||
}
|
||||
|
||||
void WorkspacePanel::onAnimationFrame()
|
||||
{
|
||||
if (animation() == ANI_DROPAREA) {
|
||||
adjustTime(m_leftTime, JI_LEFT);
|
||||
adjustTime(m_topTime, JI_TOP);
|
||||
adjustTime(m_rightTime, JI_RIGHT);
|
||||
adjustTime(m_bottomTime, JI_BOTTOM);
|
||||
layout();
|
||||
}
|
||||
}
|
||||
|
||||
void WorkspacePanel::adjustTime(int& time, int flag)
|
||||
{
|
||||
if (m_dropArea & flag) {
|
||||
if (time < ANI_DROPAREA_TICKS)
|
||||
++time;
|
||||
}
|
||||
else if (time > 0)
|
||||
--time;
|
||||
}
|
||||
|
||||
bool WorkspacePanel::dropViewAt(const gfx::Point& pos, WorkspacePanel* from, WorkspaceView* view)
|
||||
{
|
||||
int dropArea = calculateDropArea(pos);
|
||||
if (!dropArea)
|
||||
return false;
|
||||
|
||||
// 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).
|
||||
if (from == this && m_views.size() == 1)
|
||||
return false;
|
||||
|
||||
int splitterAlign = 0;
|
||||
if (dropArea & (JI_LEFT | JI_RIGHT)) splitterAlign = JI_HORIZONTAL;
|
||||
else if (dropArea & (JI_TOP | JI_BOTTOM)) splitterAlign = JI_VERTICAL;
|
||||
|
||||
ASSERT(from);
|
||||
from->removeView(view);
|
||||
|
||||
WorkspaceTabs* newTabs = new WorkspaceTabs(m_tabs->getDelegate());
|
||||
WorkspacePanel* newPanel = new WorkspacePanel(SUB_PANEL);
|
||||
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();
|
||||
if (parent->getType() == kBoxWidget) {
|
||||
self = parent;
|
||||
parent = self->getParent();
|
||||
ASSERT(parent->getType() == kSplitterWidget);
|
||||
}
|
||||
if (parent->getType() == Workspace::Type() ||
|
||||
parent->getType() == kSplitterWidget) {
|
||||
parent->replaceChild(self, splitter);
|
||||
}
|
||||
else {
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
switch (dropArea) {
|
||||
case JI_LEFT:
|
||||
case JI_TOP:
|
||||
splitter->setPosition(30);
|
||||
splitter->addChild(side);
|
||||
splitter->addChild(self);
|
||||
break;
|
||||
case JI_RIGHT:
|
||||
case JI_BOTTOM:
|
||||
splitter->setPosition(70);
|
||||
splitter->addChild(self);
|
||||
splitter->addChild(side);
|
||||
break;
|
||||
}
|
||||
|
||||
newPanel->addView(view);
|
||||
parent->layout();
|
||||
return true;
|
||||
}
|
||||
|
||||
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) {
|
||||
return JI_LEFT;
|
||||
}
|
||||
else if (top < threshold && top < left && top < right && top < bottom) {
|
||||
return JI_TOP;
|
||||
}
|
||||
else if (right < threshold && right < left && right < top && right < bottom) {
|
||||
return JI_RIGHT;
|
||||
}
|
||||
else if (bottom < threshold && bottom < left && bottom < top && bottom < right) {
|
||||
return JI_BOTTOM;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (widget->getType() == Workspace::Type())
|
||||
return static_cast<Workspace*>(widget);
|
||||
|
||||
widget = widget->getParent();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace app
|
90
src/app/ui/workspace_panel.h
Normal file
90
src/app/ui/workspace_panel.h
Normal file
@ -0,0 +1,90 @@
|
||||
// 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.
|
||||
|
||||
#ifndef APP_UI_WORKSPACE_PANEL_H_INCLUDED
|
||||
#define APP_UI_WORKSPACE_PANEL_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/ui/animated_widget.h"
|
||||
#include "app/ui/workspace_views.h"
|
||||
#include "base/signal.h"
|
||||
#include "ui/widget.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace app {
|
||||
class Tabs;
|
||||
class Workspace;
|
||||
|
||||
class WorkspacePanel : public ui::Widget
|
||||
, public AnimatedWidget {
|
||||
enum Ani : int {
|
||||
ANI_NONE,
|
||||
ANI_DROPAREA,
|
||||
};
|
||||
|
||||
public:
|
||||
typedef WorkspaceViews::iterator iterator;
|
||||
|
||||
enum PanelType {
|
||||
MAIN_PANEL,
|
||||
SUB_PANEL,
|
||||
};
|
||||
|
||||
static ui::WidgetType Type();
|
||||
|
||||
WorkspacePanel(PanelType panelType);
|
||||
~WorkspacePanel();
|
||||
|
||||
void setTabsBar(Tabs* tabs);
|
||||
|
||||
iterator begin() { return m_views.begin(); }
|
||||
iterator end() { return m_views.end(); }
|
||||
|
||||
bool isEmpty() const { return m_views.empty(); }
|
||||
|
||||
void addView(WorkspaceView* view, int pos = -1);
|
||||
void removeView(WorkspaceView* view);
|
||||
|
||||
WorkspaceView* activeView();
|
||||
void setActiveView(WorkspaceView* view);
|
||||
|
||||
// Drop views into workspace
|
||||
void setDropViewPreview(const gfx::Point& pos);
|
||||
void removeDropViewPreview();
|
||||
|
||||
// Returns true if the view was docked inside the panel.
|
||||
bool dropViewAt(const gfx::Point& pos, WorkspacePanel* from, WorkspaceView* view);
|
||||
|
||||
protected:
|
||||
void onPaint(ui::PaintEvent& ev) override;
|
||||
void onResize(ui::ResizeEvent& ev) override;
|
||||
void onAnimationFrame() override;
|
||||
void onAnimationStop() override;
|
||||
|
||||
private:
|
||||
int calculateDropArea(const gfx::Point& pos) const;
|
||||
int getDropThreshold() const;
|
||||
void adjustTime(int& time, int flag);
|
||||
void adjustActiveViewBounds();
|
||||
Workspace* getWorkspace();
|
||||
|
||||
PanelType m_panelType;
|
||||
Tabs* m_tabs;
|
||||
WorkspaceViews m_views;
|
||||
WorkspaceView* m_activeView;
|
||||
int m_dropArea;
|
||||
int m_leftTime, m_rightTime;
|
||||
int m_topTime, m_bottomTime;
|
||||
};
|
||||
|
||||
typedef std::vector<WorkspacePanel*> WorkspacePanels;
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
32
src/app/ui/workspace_tabs.cpp
Normal file
32
src/app/ui/workspace_tabs.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
// 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_tabs.h"
|
||||
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace app::skin;
|
||||
using namespace ui;
|
||||
|
||||
WorkspaceTabs::WorkspaceTabs(TabsDelegate* tabsDelegate)
|
||||
: Tabs(tabsDelegate)
|
||||
{
|
||||
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
|
||||
setBgColor(theme->colors.workspace());
|
||||
}
|
||||
|
||||
WorkspaceTabs::~WorkspaceTabs()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace app
|
25
src/app/ui/workspace_tabs.h
Normal file
25
src/app/ui/workspace_tabs.h
Normal file
@ -0,0 +1,25 @@
|
||||
// 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.
|
||||
|
||||
#ifndef APP_UI_WORKSPACE_TABS_H_INCLUDED
|
||||
#define APP_UI_WORKSPACE_TABS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/ui/tabs.h"
|
||||
|
||||
namespace app {
|
||||
class Tabs;
|
||||
|
||||
class WorkspaceTabs : public Tabs {
|
||||
public:
|
||||
WorkspaceTabs(TabsDelegate* tabsDelegate);
|
||||
~WorkspaceTabs();
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user