mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-21 07:20:46 +00:00
Add floating tabs in Tabs widget
This commit is contained in:
parent
bab5d0c7e5
commit
e506387dad
@ -9,13 +9,16 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "app/ui/tabs.h"
|
||||||
|
|
||||||
#include "app/modules/gfx.h"
|
#include "app/modules/gfx.h"
|
||||||
#include "app/modules/gui.h"
|
#include "app/modules/gui.h"
|
||||||
#include "app/ui/skin/skin_theme.h"
|
#include "app/ui/skin/skin_theme.h"
|
||||||
#include "app/ui/skin/style.h"
|
#include "app/ui/skin/style.h"
|
||||||
#include "app/ui/tabs.h"
|
|
||||||
#include "she/font.h"
|
#include "she/font.h"
|
||||||
|
#include "she/scoped_surface_lock.h"
|
||||||
#include "she/surface.h"
|
#include "she/surface.h"
|
||||||
|
#include "she/system.h"
|
||||||
#include "ui/intern.h"
|
#include "ui/intern.h"
|
||||||
#include "ui/ui.h"
|
#include "ui/ui.h"
|
||||||
|
|
||||||
@ -62,6 +65,8 @@ Tabs::Tabs(TabsDelegate* delegate)
|
|||||||
, m_ani(ANI_NONE)
|
, m_ani(ANI_NONE)
|
||||||
, m_removedTab(nullptr)
|
, m_removedTab(nullptr)
|
||||||
, m_isDragging(false)
|
, m_isDragging(false)
|
||||||
|
, m_floatingTab(nullptr)
|
||||||
|
, m_floatingOverlay(nullptr)
|
||||||
{
|
{
|
||||||
setDoubleBuffered(true);
|
setDoubleBuffered(true);
|
||||||
initTheme();
|
initTheme();
|
||||||
@ -71,7 +76,7 @@ Tabs::~Tabs()
|
|||||||
{
|
{
|
||||||
if (m_removedTab) {
|
if (m_removedTab) {
|
||||||
delete m_removedTab;
|
delete m_removedTab;
|
||||||
m_removedTab = NULL;
|
m_removedTab = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop animation
|
// Stop animation
|
||||||
@ -147,15 +152,16 @@ void Tabs::updateTabs()
|
|||||||
tabWidth = MAX(4*ui::guiscale(), tabWidth);
|
tabWidth = MAX(4*ui::guiscale(), tabWidth);
|
||||||
}
|
}
|
||||||
double x = 0.0;
|
double x = 0.0;
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (Tab* tab : m_list) {
|
for (Tab* tab : m_list) {
|
||||||
|
if (tab == m_floatingTab)
|
||||||
|
continue;
|
||||||
|
|
||||||
tab->text = tab->view->getTabText();
|
tab->text = tab->view->getTabText();
|
||||||
tab->icon = tab->view->getTabIcon();
|
tab->icon = tab->view->getTabIcon();
|
||||||
tab->x = int(x);
|
tab->x = int(x);
|
||||||
tab->width = int(x+tabWidth) - int(x);
|
tab->width = int(x+tabWidth) - int(x);
|
||||||
x += tabWidth;
|
x += tabWidth;
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
@ -236,17 +242,38 @@ bool Tabs::onProcessMessage(Message* msg)
|
|||||||
}
|
}
|
||||||
// We are drag a tab...
|
// We are drag a tab...
|
||||||
else {
|
else {
|
||||||
m_selected->x = m_dragTabX + delta.x;
|
Tab* justDocked = nullptr;
|
||||||
|
|
||||||
int i = (mousePos.x-m_border*guiscale()) / m_selected->width;
|
// Floating tab (to create a new window)
|
||||||
i = MID(0, i, int(m_list.size())-1);
|
if (ABS(delta.y) > 16*guiscale()) {
|
||||||
if (i != m_dragTabIndex) {
|
if (!m_floatingOverlay)
|
||||||
std::swap(m_list[m_dragTabIndex], m_list[i]);
|
createFloatingTab(m_selected);
|
||||||
m_dragTabIndex = i;
|
|
||||||
|
|
||||||
resetOldPositions(double(m_ani_t) / double(m_ani_T));
|
m_floatingOverlay->moveOverlay(mousePos - m_dragOffset);
|
||||||
updateTabs();
|
}
|
||||||
startAni(ANI_REORDER_TABS, ANI_REORDER_TABS_TICKS);
|
else {
|
||||||
|
justDocked = m_floatingTab;
|
||||||
|
destroyFloatingTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Docked tab
|
||||||
|
if (!m_floatingOverlay) {
|
||||||
|
m_selected->x = m_dragTabX + delta.x;
|
||||||
|
|
||||||
|
int i = (mousePos.x-m_border*guiscale()) / m_selected->width;
|
||||||
|
i = MID(0, i, int(m_list.size())-1);
|
||||||
|
if (i != m_dragTabIndex) {
|
||||||
|
m_list.erase(m_list.begin()+m_dragTabIndex);
|
||||||
|
m_list.insert(m_list.begin()+i, m_selected);
|
||||||
|
m_dragTabIndex = i;
|
||||||
|
|
||||||
|
resetOldPositions(double(m_ani_t) / double(m_ani_T));
|
||||||
|
updateTabs();
|
||||||
|
startAni(ANI_REORDER_TABS, ANI_REORDER_TABS_TICKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (justDocked)
|
||||||
|
justDocked->oldX = m_dragTabX + delta.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidate();
|
invalidate();
|
||||||
@ -255,16 +282,18 @@ bool Tabs::onProcessMessage(Message* msg)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case kMouseLeaveMessage:
|
case kMouseLeaveMessage:
|
||||||
if (m_hot != NULL) {
|
if (m_hot) {
|
||||||
m_hot = NULL;
|
m_hot = nullptr;
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case kMouseDownMessage:
|
case kMouseDownMessage:
|
||||||
if (m_hot != NULL) {
|
if (m_hot) {
|
||||||
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
||||||
m_dragMousePos = mouseMsg->position();
|
m_dragMousePos = mouseMsg->position();
|
||||||
|
m_dragOffset = mouseMsg->position() -
|
||||||
|
(getBounds().getOrigin() + getTabBounds(m_hot).getOrigin());
|
||||||
|
|
||||||
if (m_hotCloseButton) {
|
if (m_hotCloseButton) {
|
||||||
if (!m_clickedCloseButton) {
|
if (!m_clickedCloseButton) {
|
||||||
@ -360,46 +389,34 @@ void Tabs::onPaint(PaintEvent& ev)
|
|||||||
drawFiller(g, box);
|
drawFiller(g, box);
|
||||||
|
|
||||||
int startX = m_border*guiscale();
|
int startX = m_border*guiscale();
|
||||||
double t = double(m_ani_t)/double(m_ani_T);
|
|
||||||
|
|
||||||
// For each tab...
|
// For each tab...
|
||||||
int i = 0;
|
|
||||||
for (Tab* tab : m_list) {
|
for (Tab* tab : m_list) {
|
||||||
if (m_ani == ANI_NONE) {
|
if (tab == m_floatingTab)
|
||||||
box.x = startX + tab->x;
|
continue;
|
||||||
box.w = tab->width;
|
|
||||||
}
|
box = getTabBounds(tab);
|
||||||
else {
|
|
||||||
box.x = startX + int(inbetween(tab->oldX, tab->x, t));
|
|
||||||
box.w = int(inbetween(tab->oldWidth, tab->width, t));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tab != m_selected)
|
if (tab != m_selected)
|
||||||
drawTab(g, box, tab, 0, (tab == m_hot), false);
|
drawTab(g, box, tab, 0, (tab == m_hot), false);
|
||||||
|
|
||||||
box.x = box.x2();
|
box.x = box.x2();
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw deleted tab
|
// Draw deleted tab
|
||||||
if (m_ani == ANI_REMOVING_TAB && m_removedTab) {
|
if (m_ani == ANI_REMOVING_TAB && m_removedTab) {
|
||||||
box.x = startX + m_removedTab->x;
|
m_removedTab->width = 0;
|
||||||
box.w = int(inbetween(m_removedTab->oldWidth, 0, t));
|
box = getTabBounds(m_removedTab);
|
||||||
drawTab(g, box, m_removedTab, 0, false, false);
|
drawTab(g, box, m_removedTab, 0,
|
||||||
|
(m_removedTab == m_floatingTab),
|
||||||
|
(m_removedTab == m_floatingTab));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tab that is being dragged
|
// Tab that is being dragged
|
||||||
if (m_selected) {
|
if (m_selected && m_selected != m_floatingTab) {
|
||||||
|
double t = double(m_ani_t) / double(m_ani_T);
|
||||||
Tab* tab = m_selected;
|
Tab* tab = m_selected;
|
||||||
|
box = getTabBounds(tab);
|
||||||
if (m_ani == ANI_NONE) {
|
|
||||||
box.x = startX + tab->x;
|
|
||||||
box.w = tab->width;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
box.x = startX + int(inbetween(tab->oldX, tab->x, t));
|
|
||||||
box.w = int(inbetween(tab->oldWidth, tab->width, t));
|
|
||||||
}
|
|
||||||
|
|
||||||
int dy = 0;
|
int dy = 0;
|
||||||
if (m_ani == ANI_ADDING_TAB)
|
if (m_ani == ANI_ADDING_TAB)
|
||||||
@ -442,11 +459,6 @@ void Tabs::drawTab(Graphics* g, const gfx::Rect& _box, Tab* tab, int dy,
|
|||||||
bool hover, bool selected)
|
bool hover, bool selected)
|
||||||
{
|
{
|
||||||
gfx::Rect box = _box;
|
gfx::Rect box = _box;
|
||||||
|
|
||||||
// Is the tab outside the bounds of the widget?
|
|
||||||
if (box.x >= getBounds().x2() || box.x2() <= getBounds().x)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (box.w < ui::guiscale()*8)
|
if (box.w < ui::guiscale()*8)
|
||||||
box.w = ui::guiscale()*8;
|
box.w = ui::guiscale()*8;
|
||||||
|
|
||||||
@ -587,6 +599,9 @@ void Tabs::calculateHot()
|
|||||||
|
|
||||||
// For each tab
|
// For each tab
|
||||||
for (Tab* tab : m_list) {
|
for (Tab* tab : m_list) {
|
||||||
|
if (tab == m_floatingTab)
|
||||||
|
continue;
|
||||||
|
|
||||||
box.w = tab->width;
|
box.w = tab->width;
|
||||||
|
|
||||||
if (box.contains(mousePos)) {
|
if (box.contains(mousePos)) {
|
||||||
@ -625,6 +640,9 @@ gfx::Rect Tabs::getTabCloseButtonBounds(Tab* tab, const gfx::Rect& box)
|
|||||||
void Tabs::resetOldPositions()
|
void Tabs::resetOldPositions()
|
||||||
{
|
{
|
||||||
for (Tab* tab : m_list) {
|
for (Tab* tab : m_list) {
|
||||||
|
if (tab == m_floatingTab)
|
||||||
|
continue;
|
||||||
|
|
||||||
tab->oldX = tab->x;
|
tab->oldX = tab->x;
|
||||||
tab->oldWidth = tab->width;
|
tab->oldWidth = tab->width;
|
||||||
}
|
}
|
||||||
@ -633,6 +651,9 @@ void Tabs::resetOldPositions()
|
|||||||
void Tabs::resetOldPositions(double t)
|
void Tabs::resetOldPositions(double t)
|
||||||
{
|
{
|
||||||
for (Tab* tab : m_list) {
|
for (Tab* tab : m_list) {
|
||||||
|
if (tab == m_floatingTab)
|
||||||
|
continue;
|
||||||
|
|
||||||
tab->oldX = int(inbetween(tab->oldX, tab->x, t));
|
tab->oldX = int(inbetween(tab->oldX, tab->x, t));
|
||||||
tab->oldWidth = int(inbetween(tab->oldWidth, tab->width, t));
|
tab->oldWidth = int(inbetween(tab->oldWidth, tab->width, t));
|
||||||
}
|
}
|
||||||
@ -677,6 +698,8 @@ void Tabs::stopDrag()
|
|||||||
{
|
{
|
||||||
m_isDragging = false;
|
m_isDragging = false;
|
||||||
|
|
||||||
|
destroyFloatingTab();
|
||||||
|
|
||||||
if (m_selected) {
|
if (m_selected) {
|
||||||
m_selected->oldX = m_selected->x;
|
m_selected->oldX = m_selected->x;
|
||||||
m_selected->oldWidth = m_selected->width;
|
m_selected->oldWidth = m_selected->width;
|
||||||
@ -687,4 +710,83 @@ void Tabs::stopDrag()
|
|||||||
startAni(ANI_REORDER_TABS, ANI_REORDER_TABS_TICKS);
|
startAni(ANI_REORDER_TABS, ANI_REORDER_TABS_TICKS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gfx::Rect Tabs::getTabBounds(Tab* tab)
|
||||||
|
{
|
||||||
|
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
|
||||||
|
gfx::Rect rect = getClientBounds();
|
||||||
|
gfx::Rect box(rect.x, rect.y, rect.w,
|
||||||
|
(m_list.empty() && m_ani == ANI_NONE ? 0:
|
||||||
|
theme->dimensions.tabsHeight() - theme->dimensions.tabsEmptyHeight()));
|
||||||
|
int startX = m_border*guiscale();
|
||||||
|
double t = double(m_ani_t) / double(m_ani_T);
|
||||||
|
|
||||||
|
if (m_ani == ANI_NONE) {
|
||||||
|
box.x = startX + tab->x;
|
||||||
|
box.w = tab->width;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
box.x = startX + int(inbetween(tab->oldX, tab->x, t));
|
||||||
|
box.w = int(inbetween(tab->oldWidth, tab->width, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tabs::createFloatingTab(Tab* tab)
|
||||||
|
{
|
||||||
|
ASSERT(!m_floatingOverlay);
|
||||||
|
|
||||||
|
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
|
||||||
|
she::Surface* surface = she::instance()->createRgbaSurface(
|
||||||
|
tab->width, theme->dimensions.tabsHeight());
|
||||||
|
|
||||||
|
{
|
||||||
|
Graphics g(surface, 0, 0);
|
||||||
|
g.setFont(getFont());
|
||||||
|
g.fillRect(gfx::ColorNone, g.getClipBounds());
|
||||||
|
drawTab(&g, g.getClipBounds(), tab, 0, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make opaque (TODO this shouldn't be necessary)
|
||||||
|
{
|
||||||
|
she::ScopedSurfaceLock lock(surface);
|
||||||
|
for (int y=0; y<surface->height(); ++y)
|
||||||
|
for (int x=0; x<surface->width(); ++x) {
|
||||||
|
gfx::Color c = lock->getPixel(x, y);
|
||||||
|
lock->putPixel(gfx::seta(c, 255), x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_floatingOverlay = new Overlay(surface, gfx::Point(), Overlay::MouseZOrder-1);
|
||||||
|
OverlayManager::instance()->addOverlay(m_floatingOverlay);
|
||||||
|
|
||||||
|
resetOldPositions();
|
||||||
|
|
||||||
|
m_floatingTab = tab;
|
||||||
|
m_removedTab = nullptr;
|
||||||
|
startAni(ANI_REMOVING_TAB, ANI_REMOVING_TAB_TICKS);
|
||||||
|
updateTabs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tabs::destroyFloatingTab()
|
||||||
|
{
|
||||||
|
if (m_floatingOverlay) {
|
||||||
|
OverlayManager::instance()->removeOverlay(m_floatingOverlay);
|
||||||
|
delete m_floatingOverlay;
|
||||||
|
m_floatingOverlay = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_floatingTab) {
|
||||||
|
Tab* tab = m_floatingTab;
|
||||||
|
m_floatingTab = nullptr;
|
||||||
|
|
||||||
|
resetOldPositions();
|
||||||
|
startAni(ANI_ADDING_TAB, ANI_ADDING_TAB_TICKS);
|
||||||
|
updateTabs();
|
||||||
|
|
||||||
|
tab->oldX = tab->x;
|
||||||
|
tab->oldWidth = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
class Graphics;
|
class Graphics;
|
||||||
|
class Overlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
@ -119,6 +120,9 @@ namespace app {
|
|||||||
gfx::Rect getTabCloseButtonBounds(Tab* tab, const gfx::Rect& box);
|
gfx::Rect getTabCloseButtonBounds(Tab* tab, const gfx::Rect& box);
|
||||||
void startDrag();
|
void startDrag();
|
||||||
void stopDrag();
|
void stopDrag();
|
||||||
|
gfx::Rect getTabBounds(Tab* tab);
|
||||||
|
void createFloatingTab(Tab* tab);
|
||||||
|
void destroyFloatingTab();
|
||||||
|
|
||||||
int m_border;
|
int m_border;
|
||||||
TabsList m_list;
|
TabsList m_list;
|
||||||
@ -141,7 +145,10 @@ namespace app {
|
|||||||
bool m_isDragging;
|
bool m_isDragging;
|
||||||
int m_dragTabX;
|
int m_dragTabX;
|
||||||
gfx::Point m_dragMousePos;
|
gfx::Point m_dragMousePos;
|
||||||
|
gfx::Point m_dragOffset;
|
||||||
int m_dragTabIndex;
|
int m_dragTabIndex;
|
||||||
|
Tab* m_floatingTab;
|
||||||
|
ui::Overlay* m_floatingOverlay;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// SHE library
|
// SHE library
|
||||||
// Copyright (C) 2012-2014 David Capello
|
// Copyright (C) 2012-2015 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
// Read LICENSE.txt for more information.
|
// Read LICENSE.txt for more information.
|
||||||
@ -55,10 +55,16 @@ void checked_mode(int offset)
|
|||||||
namespace she {
|
namespace she {
|
||||||
|
|
||||||
inline int to_allegro(int color_depth, gfx::Color color) {
|
inline int to_allegro(int color_depth, gfx::Color color) {
|
||||||
if (gfx::is_transparent(color))
|
if (color_depth == 32) {
|
||||||
return -1;
|
return makeacol32(
|
||||||
else
|
gfx::getr(color), gfx::getg(color), gfx::getb(color), gfx::geta(color));
|
||||||
return makecol_depth(color_depth, gfx::getr(color), gfx::getg(color), gfx::getb(color));
|
}
|
||||||
|
else {
|
||||||
|
if (gfx::is_transparent(color))
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return makecol_depth(color_depth, gfx::getr(color), gfx::getg(color), gfx::getb(color));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline gfx::Color from_allegro(int color_depth, int color) {
|
inline gfx::Color from_allegro(int color_depth, int color) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user