2015-02-12 23:16:25 +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.
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#ifdef HAVE_CONFIG_H
|
2007-11-28 22:19:36 +08:00
|
|
|
#include "config.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#endif
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/modules/gfx.h"
|
|
|
|
#include "app/modules/gui.h"
|
|
|
|
#include "app/ui/skin/skin_theme.h"
|
2015-02-18 03:03:00 +08:00
|
|
|
#include "app/ui/skin/style.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/ui/tabs.h"
|
2014-06-23 05:53:14 +08:00
|
|
|
#include "she/font.h"
|
|
|
|
#include "she/surface.h"
|
2012-06-18 09:49:58 +08:00
|
|
|
#include "ui/intern.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "ui/ui.h"
|
2012-06-18 09:02:54 +08:00
|
|
|
|
2014-06-23 05:53:14 +08:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cmath>
|
|
|
|
|
2010-07-20 11:59:52 +08:00
|
|
|
#define ANI_ADDING_TAB_TICKS 5
|
|
|
|
#define ANI_REMOVING_TAB_TICKS 10
|
2015-02-25 07:10:22 +08:00
|
|
|
#define ANI_REORDER_TABS_TICKS 5
|
2010-07-20 11:59:52 +08:00
|
|
|
|
2011-01-22 05:54:47 +08:00
|
|
|
#define HAS_ARROWS(tabs) ((m_button_left->getParent() == (tabs)))
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
namespace app {
|
|
|
|
|
|
|
|
using namespace app::skin;
|
|
|
|
using namespace ui;
|
|
|
|
|
2015-02-22 07:06:35 +08:00
|
|
|
namespace {
|
|
|
|
double ease(double t) {
|
|
|
|
return (1.0 - std::pow(1.0 - t, 2));
|
|
|
|
}
|
|
|
|
double inbetween(double x0, double x1, double t) {
|
|
|
|
return x0 + (x1-x0)*ease(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-04 09:07:24 +08:00
|
|
|
static WidgetType tabs_type()
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2013-04-04 09:07:24 +08:00
|
|
|
static WidgetType type = kGenericWidget;
|
|
|
|
if (type == kGenericWidget)
|
|
|
|
type = register_widget_type();
|
2010-06-14 04:51:22 +08:00
|
|
|
return type;
|
|
|
|
}
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2011-04-03 01:57:40 +08:00
|
|
|
Tabs::Tabs(TabsDelegate* delegate)
|
2010-06-14 04:51:22 +08:00
|
|
|
: Widget(tabs_type())
|
2015-02-22 07:06:35 +08:00
|
|
|
, m_border(2)
|
2011-04-03 01:57:40 +08:00
|
|
|
, m_delegate(delegate)
|
2012-07-06 12:06:00 +08:00
|
|
|
, m_timer(1000/60, this)
|
2015-02-25 07:10:22 +08:00
|
|
|
, m_isDragging(false)
|
2010-06-14 04:51:22 +08:00
|
|
|
{
|
2013-05-21 07:40:18 +08:00
|
|
|
setDoubleBuffered(true);
|
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
m_hot = NULL;
|
2015-02-20 00:13:25 +08:00
|
|
|
m_hotCloseButton = false;
|
2010-06-14 04:51:22 +08:00
|
|
|
m_selected = NULL;
|
2010-07-20 11:59:52 +08:00
|
|
|
m_ani = ANI_NONE;
|
2010-07-27 03:52:40 +08:00
|
|
|
m_removedTab = NULL;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2011-02-15 20:00:29 +08:00
|
|
|
initTheme();
|
2010-06-14 04:51:22 +08:00
|
|
|
}
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
Tabs::~Tabs()
|
|
|
|
{
|
2010-07-27 03:52:40 +08:00
|
|
|
if (m_removedTab) {
|
|
|
|
delete m_removedTab;
|
|
|
|
m_removedTab = NULL;
|
|
|
|
}
|
|
|
|
|
2010-07-20 11:59:52 +08:00
|
|
|
// Stop animation
|
|
|
|
stopAni();
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2010-07-20 11:59:52 +08:00
|
|
|
// Remove all tabs
|
2015-02-15 22:45:23 +08:00
|
|
|
for (Tab* tab : m_list)
|
|
|
|
delete tab;
|
|
|
|
m_list.clear();
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
void Tabs::addTab(TabView* tabView)
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2015-02-25 07:10:22 +08:00
|
|
|
resetOldPositions();
|
2015-02-22 07:06:35 +08:00
|
|
|
startAni(ANI_ADDING_TAB, ANI_ADDING_TAB_TICKS);
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2015-02-22 07:06:35 +08:00
|
|
|
Tab* tab = new Tab(tabView);
|
2015-02-15 22:45:23 +08:00
|
|
|
m_list.push_back(tab);
|
2015-02-22 07:06:35 +08:00
|
|
|
updateTabs();
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2015-02-22 07:06:35 +08:00
|
|
|
tab->oldX = tab->x;
|
|
|
|
tab->oldWidth = tab->width;
|
|
|
|
tab->modified = false;
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
void Tabs::removeTab(TabView* tabView)
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2013-01-21 05:40:37 +08:00
|
|
|
Tab* tab = getTabByView(tabView);
|
|
|
|
if (!tab)
|
|
|
|
return;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2014-02-03 07:55:32 +08:00
|
|
|
if (m_hot == tab)
|
2015-02-22 07:06:35 +08:00
|
|
|
m_hot = nullptr;
|
2014-02-03 07:55:32 +08:00
|
|
|
|
|
|
|
if (m_selected == tab) {
|
2015-02-23 08:18:53 +08:00
|
|
|
if (tab == m_list.back())
|
|
|
|
selectPreviousTab();
|
|
|
|
else
|
|
|
|
selectNextTab();
|
|
|
|
|
2014-02-03 07:55:32 +08:00
|
|
|
if (m_selected == tab)
|
2015-02-22 07:06:35 +08:00
|
|
|
m_selected = nullptr;
|
2014-02-03 07:55:32 +08:00
|
|
|
}
|
2009-11-22 22:14:06 +08:00
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
TabsListIterator it =
|
2015-02-15 22:45:23 +08:00
|
|
|
std::find(m_list.begin(), m_list.end(), tab);
|
|
|
|
ASSERT(it != m_list.end() && "Removing a tab that is not part of the Tabs widget");
|
|
|
|
it = m_list.erase(it);
|
2010-07-20 11:59:52 +08:00
|
|
|
|
2015-02-22 07:06:35 +08:00
|
|
|
delete m_removedTab;
|
2013-01-21 05:40:37 +08:00
|
|
|
m_removedTab = tab;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2015-02-22 07:06:35 +08:00
|
|
|
if (m_delegate)
|
2015-02-23 08:18:53 +08:00
|
|
|
tab->modified = m_delegate->onIsModified(this, tabView);
|
2015-02-22 07:06:35 +08:00
|
|
|
tab->view = nullptr; // The view will be destroyed after Tabs::removeTab() anyway
|
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
// Next tab in the list
|
2015-02-15 22:45:23 +08:00
|
|
|
if (it != m_list.end())
|
2013-01-21 05:40:37 +08:00
|
|
|
m_nextTabOfTheRemovedOne = *it;
|
|
|
|
else
|
2015-02-22 07:06:35 +08:00
|
|
|
m_nextTabOfTheRemovedOne = nullptr;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2015-02-25 07:10:22 +08:00
|
|
|
resetOldPositions();
|
2015-02-22 07:06:35 +08:00
|
|
|
startAni(ANI_REMOVING_TAB, ANI_REMOVING_TAB_TICKS);
|
|
|
|
updateTabs();
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2015-02-22 07:06:35 +08:00
|
|
|
void Tabs::updateTabs()
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2015-02-22 07:06:35 +08:00
|
|
|
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
|
|
|
|
double availWidth = getBounds().w - m_border*ui::guiscale();
|
|
|
|
double defTabWidth = theme->dimensions.tabsWidth();
|
|
|
|
double tabWidth = defTabWidth;
|
|
|
|
if (tabWidth * m_list.size() > availWidth) {
|
|
|
|
tabWidth = availWidth / double(m_list.size());
|
|
|
|
tabWidth = MAX(4*ui::guiscale(), tabWidth);
|
|
|
|
}
|
|
|
|
double x = 0.0;
|
2015-02-25 07:10:22 +08:00
|
|
|
int i = 0;
|
2015-02-22 07:06:35 +08:00
|
|
|
|
2015-02-20 08:44:22 +08:00
|
|
|
for (Tab* tab : m_list) {
|
2015-02-20 00:13:25 +08:00
|
|
|
tab->text = tab->view->getTabText();
|
2015-02-20 08:44:22 +08:00
|
|
|
tab->icon = tab->view->getTabIcon();
|
2015-02-22 07:06:35 +08:00
|
|
|
tab->x = int(x);
|
2015-02-23 21:52:04 +08:00
|
|
|
tab->width = int(x+tabWidth) - int(x);
|
|
|
|
x += tabWidth;
|
2015-02-25 07:10:22 +08:00
|
|
|
++i;
|
2015-02-20 08:44:22 +08:00
|
|
|
}
|
2013-01-21 05:40:37 +08:00
|
|
|
invalidate();
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
void Tabs::selectTab(TabView* tabView)
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2013-03-28 08:19:35 +08:00
|
|
|
ASSERT(tabView != NULL);
|
|
|
|
|
2015-02-22 07:06:35 +08:00
|
|
|
Tab* tab = getTabByView(tabView);
|
|
|
|
if (tab)
|
2012-02-12 22:33:06 +08:00
|
|
|
selectTabInternal(tab);
|
|
|
|
}
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2012-02-12 22:33:06 +08:00
|
|
|
void Tabs::selectNextTab()
|
|
|
|
{
|
2013-01-21 05:40:37 +08:00
|
|
|
TabsListIterator currentTabIt = getTabIteratorByView(m_selected->view);
|
2012-02-12 22:33:06 +08:00
|
|
|
TabsListIterator it = currentTabIt;
|
2015-02-15 22:45:23 +08:00
|
|
|
if (it != m_list.end()) {
|
2012-02-12 22:33:06 +08:00
|
|
|
// If we are at the end of the list, cycle to the first tab.
|
2015-02-15 22:45:23 +08:00
|
|
|
if (it == --m_list.end())
|
|
|
|
it = m_list.begin();
|
2012-02-12 22:33:06 +08:00
|
|
|
// Go to next tab.
|
|
|
|
else
|
|
|
|
++it;
|
|
|
|
|
2015-02-25 20:00:19 +08:00
|
|
|
if (it != currentTabIt)
|
2012-02-12 22:33:06 +08:00
|
|
|
selectTabInternal(*it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Tabs::selectPreviousTab()
|
|
|
|
{
|
2013-01-21 05:40:37 +08:00
|
|
|
TabsListIterator currentTabIt = getTabIteratorByView(m_selected->view);
|
2012-02-12 22:33:06 +08:00
|
|
|
TabsListIterator it = currentTabIt;
|
2015-02-15 22:45:23 +08:00
|
|
|
if (it != m_list.end()) {
|
2012-02-12 22:33:06 +08:00
|
|
|
// If we are at the beginning of the list, cycle to the last tab.
|
2015-02-15 22:45:23 +08:00
|
|
|
if (it == m_list.begin())
|
|
|
|
it = --m_list.end();
|
2012-02-12 22:33:06 +08:00
|
|
|
// Go to previous tab.
|
|
|
|
else
|
|
|
|
--it;
|
|
|
|
|
2015-02-25 20:00:19 +08:00
|
|
|
if (it != currentTabIt)
|
2012-02-12 22:33:06 +08:00
|
|
|
selectTabInternal(*it);
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
TabView* Tabs::getSelectedTab()
|
2008-03-01 03:29:49 +08:00
|
|
|
{
|
2010-06-14 04:51:22 +08:00
|
|
|
if (m_selected != NULL)
|
2013-01-21 05:40:37 +08:00
|
|
|
return m_selected->view;
|
2008-03-01 03:29:49 +08:00
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-04-03 00:14:07 +08:00
|
|
|
bool Tabs::onProcessMessage(Message* msg)
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2013-07-29 08:17:07 +08:00
|
|
|
switch (msg->type()) {
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseEnterMessage:
|
2015-02-25 07:10:22 +08:00
|
|
|
calculateHot();
|
|
|
|
return true;
|
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseMoveMessage:
|
2010-06-14 04:51:22 +08:00
|
|
|
calculateHot();
|
2015-02-25 07:10:22 +08:00
|
|
|
|
|
|
|
if (hasCapture() && m_selected) {
|
|
|
|
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
|
|
|
gfx::Point mousePos = mouseMsg->position();
|
|
|
|
gfx::Point delta = mousePos - m_dragMousePos;
|
|
|
|
|
|
|
|
if (!m_isDragging) {
|
|
|
|
if (!m_clickedCloseButton) {
|
|
|
|
double dist = std::sqrt(delta.x*delta.x + delta.y*delta.y);
|
|
|
|
if (dist > 4.0/ui::guiscale())
|
|
|
|
startDrag();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// We are drag a tab...
|
|
|
|
else {
|
|
|
|
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) {
|
|
|
|
std::swap(m_list[m_dragTabIndex], m_list[i]);
|
|
|
|
m_dragTabIndex = i;
|
|
|
|
|
|
|
|
resetOldPositions(double(m_ani_t) / double(m_ani_T));
|
|
|
|
updateTabs();
|
|
|
|
startAni(ANI_REORDER_TABS, ANI_REORDER_TABS_TICKS);
|
|
|
|
}
|
|
|
|
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
}
|
2010-01-31 00:43:13 +08:00
|
|
|
return true;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseLeaveMessage:
|
2010-06-14 04:51:22 +08:00
|
|
|
if (m_hot != NULL) {
|
2012-01-06 06:45:03 +08:00
|
|
|
m_hot = NULL;
|
|
|
|
invalidate();
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
2010-01-31 00:43:13 +08:00
|
|
|
return true;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseDownMessage:
|
2010-06-14 04:51:22 +08:00
|
|
|
if (m_hot != NULL) {
|
2014-08-09 22:13:51 +08:00
|
|
|
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
2015-02-25 07:10:22 +08:00
|
|
|
m_dragMousePos = mouseMsg->position();
|
2014-08-09 22:13:51 +08:00
|
|
|
|
2015-02-20 00:13:25 +08:00
|
|
|
if (m_hotCloseButton) {
|
|
|
|
if (!m_clickedCloseButton) {
|
|
|
|
m_clickedCloseButton = true;
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
}
|
2015-02-23 21:52:04 +08:00
|
|
|
else if (mouseMsg->left() && m_selected != m_hot) {
|
|
|
|
m_selected = m_hot;
|
|
|
|
|
|
|
|
// Left-click is processed in mouse down message,
|
|
|
|
// right-click is processed in mouse up.
|
|
|
|
if (m_selected && m_delegate)
|
|
|
|
m_delegate->onSelectTab(this, m_selected->view);
|
2015-02-20 00:13:25 +08:00
|
|
|
|
2015-02-23 21:52:04 +08:00
|
|
|
invalidate();
|
2014-08-09 22:13:51 +08:00
|
|
|
}
|
2015-02-20 00:13:25 +08:00
|
|
|
|
|
|
|
captureMouse();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case kMouseUpMessage:
|
|
|
|
if (hasCapture()) {
|
|
|
|
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
|
|
|
|
2015-02-23 21:52:04 +08:00
|
|
|
if ((mouseMsg->middle()) ||
|
|
|
|
(mouseMsg->left() && m_hotCloseButton && m_clickedCloseButton)) {
|
|
|
|
if (m_hot && m_delegate)
|
|
|
|
m_delegate->onCloseTab(this, m_hot->view);
|
|
|
|
}
|
|
|
|
else if (mouseMsg->right() && m_hot) {
|
|
|
|
if (m_delegate)
|
|
|
|
m_delegate->onContextMenuTab(this, m_hot->view);
|
2015-02-20 00:13:25 +08:00
|
|
|
}
|
2015-02-20 19:53:52 +08:00
|
|
|
|
2015-02-20 00:13:25 +08:00
|
|
|
releaseMouse();
|
2015-02-20 19:53:52 +08:00
|
|
|
|
2015-02-25 07:10:22 +08:00
|
|
|
if (m_isDragging)
|
|
|
|
stopDrag();
|
|
|
|
|
2015-02-20 19:53:52 +08:00
|
|
|
if (m_clickedCloseButton) {
|
|
|
|
m_clickedCloseButton = false;
|
|
|
|
invalidate();
|
|
|
|
}
|
2009-11-22 22:14:06 +08:00
|
|
|
}
|
2010-01-31 00:43:13 +08:00
|
|
|
return true;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseWheelMessage: {
|
2014-04-29 09:02:56 +08:00
|
|
|
int dz =
|
2015-02-22 07:06:35 +08:00
|
|
|
(static_cast<MouseMessage*>(msg)->wheelDelta().x +
|
|
|
|
static_cast<MouseMessage*>(msg)->wheelDelta().y);
|
|
|
|
|
|
|
|
auto it = std::find(m_list.begin(), m_list.end(), m_selected);
|
|
|
|
if (it != m_list.end()) {
|
|
|
|
int index = (it - m_list.begin());
|
|
|
|
int newIndex = index + dz;
|
|
|
|
newIndex = MID(0, newIndex, int(m_list.size())-1);
|
2015-02-25 20:00:19 +08:00
|
|
|
if (newIndex != index) {
|
2015-02-22 07:06:35 +08:00
|
|
|
selectTabInternal(m_list[newIndex]);
|
2015-02-25 20:00:19 +08:00
|
|
|
}
|
2010-07-20 11:59:52 +08:00
|
|
|
}
|
2010-01-31 00:43:13 +08:00
|
|
|
return true;
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kTimerMessage: {
|
2015-02-22 07:06:35 +08:00
|
|
|
if (m_ani != ANI_NONE) {
|
|
|
|
if (m_ani_t == m_ani_T)
|
|
|
|
stopAni();
|
|
|
|
else
|
|
|
|
++m_ani_t;
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2015-02-22 07:06:35 +08:00
|
|
|
invalidate();
|
2010-07-20 11:59:52 +08:00
|
|
|
}
|
2008-01-04 07:22:04 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2010-08-03 08:29:56 +08:00
|
|
|
return Widget::onProcessMessage(msg);
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2013-05-21 07:40:18 +08:00
|
|
|
void Tabs::onPaint(PaintEvent& ev)
|
|
|
|
{
|
|
|
|
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
|
|
|
|
Graphics* g = ev.getGraphics();
|
|
|
|
gfx::Rect rect = getClientBounds();
|
2015-02-25 07:10:22 +08:00
|
|
|
gfx::Rect box(rect.x, rect.y, rect.w,
|
2015-03-03 20:07:49 +08:00
|
|
|
(m_list.empty() && m_ani == ANI_NONE ? 0:
|
2015-02-18 03:03:00 +08:00
|
|
|
theme->dimensions.tabsHeight() - theme->dimensions.tabsEmptyHeight()));
|
2013-05-21 07:40:18 +08:00
|
|
|
|
2015-02-16 02:29:16 +08:00
|
|
|
g->fillRect(theme->colors.windowFace(), g->getClipBounds());
|
2015-02-25 07:10:22 +08:00
|
|
|
drawFiller(g, box);
|
2013-05-21 07:40:18 +08:00
|
|
|
|
2015-02-25 07:10:22 +08:00
|
|
|
int startX = m_border*guiscale();
|
2015-02-22 07:06:35 +08:00
|
|
|
double t = double(m_ani_t)/double(m_ani_T);
|
|
|
|
Tab* prevTab = nullptr;
|
2013-05-21 07:40:18 +08:00
|
|
|
|
|
|
|
// For each tab...
|
2015-02-25 07:10:22 +08:00
|
|
|
int i = 0;
|
2015-02-15 22:45:23 +08:00
|
|
|
for (Tab* tab : m_list) {
|
2015-02-22 07:06:35 +08:00
|
|
|
if (m_ani == ANI_NONE) {
|
2015-02-25 07:10:22 +08:00
|
|
|
box.x = startX + tab->x;
|
2015-02-22 07:06:35 +08:00
|
|
|
box.w = tab->width;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
box.x = startX + int(inbetween(tab->oldX, tab->x, t));
|
|
|
|
box.w = int(inbetween(tab->oldWidth, tab->width, t));
|
|
|
|
}
|
2013-05-21 07:40:18 +08:00
|
|
|
|
2015-02-25 07:10:22 +08:00
|
|
|
if (m_ani == ANI_REMOVING_TAB) {
|
2015-02-22 07:06:35 +08:00
|
|
|
if (m_nextTabOfTheRemovedOne == tab) {
|
|
|
|
// Draw deleted tab
|
|
|
|
if (m_removedTab) {
|
2015-02-25 07:10:22 +08:00
|
|
|
gfx::Rect box2(box.x, box.y, 0, box.h);
|
2015-02-22 07:06:35 +08:00
|
|
|
box2.w = int(startX + inbetween(tab->oldX, tab->x, t)) - box2.x;
|
|
|
|
drawTab(g, box2, m_removedTab, 0, false, false);
|
|
|
|
}
|
2013-05-21 07:40:18 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-25 07:10:22 +08:00
|
|
|
if (tab != m_selected)
|
|
|
|
drawTab(g, box, tab, 0, (tab == m_hot), false);
|
2013-05-21 07:40:18 +08:00
|
|
|
|
|
|
|
box.x = box.x2();
|
2015-02-22 07:06:35 +08:00
|
|
|
prevTab = tab;
|
2015-02-25 07:10:22 +08:00
|
|
|
++i;
|
2013-05-21 07:40:18 +08:00
|
|
|
}
|
|
|
|
|
2015-02-22 07:06:35 +08:00
|
|
|
// Draw deleted tab
|
|
|
|
if (m_ani == ANI_REMOVING_TAB && !m_nextTabOfTheRemovedOne && m_removedTab) {
|
|
|
|
if (prevTab) {
|
2015-02-25 07:10:22 +08:00
|
|
|
box.x = int(startX + inbetween(
|
2015-02-22 07:06:35 +08:00
|
|
|
prevTab->oldX+prevTab->oldWidth, prevTab->x+prevTab->width, t));
|
2013-05-21 07:40:18 +08:00
|
|
|
}
|
2015-02-25 07:10:22 +08:00
|
|
|
else
|
|
|
|
box.x = startX;
|
|
|
|
box.w = int(inbetween(m_removedTab->oldWidth, 0, t));
|
|
|
|
drawTab(g, box, m_removedTab, 0, false, false);
|
2013-05-21 07:40:18 +08:00
|
|
|
}
|
|
|
|
|
2015-02-25 07:10:22 +08:00
|
|
|
// Tab that is being dragged
|
|
|
|
if (m_selected) {
|
|
|
|
Tab* tab = m_selected;
|
|
|
|
|
|
|
|
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;
|
|
|
|
if (m_ani == ANI_ADDING_TAB)
|
|
|
|
dy = int(box.h - box.h * t);
|
|
|
|
|
|
|
|
drawTab(g, box, m_selected, dy, (tab == m_hot), true);
|
2013-05-21 07:40:18 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-12 04:56:27 +08:00
|
|
|
void Tabs::onResize(ResizeEvent& ev)
|
|
|
|
{
|
|
|
|
setBoundsQuietly(ev.getBounds());
|
2015-02-22 07:06:35 +08:00
|
|
|
updateTabs();
|
2013-05-12 04:56:27 +08:00
|
|
|
}
|
|
|
|
|
2012-09-27 05:34:52 +08:00
|
|
|
void Tabs::onPreferredSize(PreferredSizeEvent& ev)
|
|
|
|
{
|
|
|
|
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
|
2015-02-17 23:22:46 +08:00
|
|
|
gfx::Size reqsize(0, 0);
|
2012-09-27 05:34:52 +08:00
|
|
|
|
2015-03-03 20:07:49 +08:00
|
|
|
if (m_list.empty() && m_ani == ANI_NONE)
|
2015-02-17 23:22:46 +08:00
|
|
|
reqsize.h = theme->dimensions.tabsEmptyHeight();
|
|
|
|
else
|
|
|
|
reqsize.h = theme->dimensions.tabsHeight();
|
2014-04-18 01:47:28 +08:00
|
|
|
|
|
|
|
ev.setPreferredSize(reqsize);
|
2012-09-27 05:34:52 +08:00
|
|
|
}
|
|
|
|
|
2012-02-12 22:33:06 +08:00
|
|
|
void Tabs::selectTabInternal(Tab* tab)
|
|
|
|
{
|
|
|
|
m_selected = tab;
|
|
|
|
makeTabVisible(tab);
|
|
|
|
invalidate();
|
2015-02-25 20:00:19 +08:00
|
|
|
|
|
|
|
if (m_delegate)
|
|
|
|
m_delegate->onSelectTab(this, tab->view);
|
2012-02-12 22:33:06 +08:00
|
|
|
}
|
|
|
|
|
2015-02-20 08:44:22 +08:00
|
|
|
void Tabs::drawTab(Graphics* g, const gfx::Rect& _box, Tab* tab, int dy,
|
2015-02-20 00:13:25 +08:00
|
|
|
bool hover, bool selected)
|
2010-07-27 03:52:40 +08:00
|
|
|
{
|
2015-02-20 00:13:25 +08:00
|
|
|
gfx::Rect box = _box;
|
|
|
|
|
2010-07-27 03:52:40 +08:00
|
|
|
// Is the tab outside the bounds of the widget?
|
2013-10-26 23:50:55 +08:00
|
|
|
if (box.x >= getBounds().x2() || box.x2() <= getBounds().x)
|
2010-07-27 03:52:40 +08:00
|
|
|
return;
|
|
|
|
|
2015-02-20 00:13:25 +08:00
|
|
|
if (box.w < ui::guiscale()*8)
|
|
|
|
box.w = ui::guiscale()*8;
|
|
|
|
|
2011-01-22 05:08:25 +08:00
|
|
|
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
|
2014-06-29 03:10:39 +08:00
|
|
|
gfx::Color text_color;
|
|
|
|
gfx::Color face_color;
|
2015-02-20 00:13:25 +08:00
|
|
|
int clipTextRightSide;
|
|
|
|
|
2015-02-23 00:06:22 +08:00
|
|
|
gfx::Rect closeBox = getTabCloseButtonBounds(tab, box);
|
2015-02-20 00:13:25 +08:00
|
|
|
if (closeBox.isEmpty())
|
|
|
|
clipTextRightSide = 4*ui::guiscale();
|
|
|
|
else {
|
2015-02-20 08:44:22 +08:00
|
|
|
closeBox.y += dy;
|
2015-02-20 00:13:25 +08:00
|
|
|
clipTextRightSide = closeBox.w;
|
|
|
|
}
|
2010-07-27 03:52:40 +08:00
|
|
|
|
|
|
|
// Selected
|
|
|
|
if (selected) {
|
2015-02-18 03:03:00 +08:00
|
|
|
text_color = theme->colors.tabActiveText();
|
|
|
|
face_color = theme->colors.tabActiveFace();
|
2010-07-27 03:52:40 +08:00
|
|
|
}
|
|
|
|
// Non-selected
|
|
|
|
else {
|
2015-02-16 02:29:16 +08:00
|
|
|
text_color = theme->colors.tabNormalText();
|
|
|
|
face_color = theme->colors.tabNormalFace();
|
2010-07-27 03:52:40 +08:00
|
|
|
}
|
|
|
|
|
2015-02-18 03:03:00 +08:00
|
|
|
skin::Style::State state;
|
|
|
|
if (selected) state += skin::Style::active();
|
2015-02-20 00:13:25 +08:00
|
|
|
if (hover) state += skin::Style::hover();
|
2015-02-18 03:03:00 +08:00
|
|
|
|
2015-02-20 08:44:22 +08:00
|
|
|
// Tab without text
|
2015-02-20 00:13:25 +08:00
|
|
|
theme->styles.tab()->paint(g,
|
2015-02-20 08:44:22 +08:00
|
|
|
gfx::Rect(box.x, box.y+dy, box.w, box.h),
|
2015-02-20 00:13:25 +08:00
|
|
|
nullptr, state);
|
|
|
|
|
2015-03-03 20:11:37 +08:00
|
|
|
{
|
|
|
|
IntersectClip clip(g, gfx::Rect(box.x, box.y+dy, box.w-clipTextRightSide, box.h));
|
|
|
|
|
|
|
|
// Tab icon
|
|
|
|
TabIcon icon = tab->icon;
|
|
|
|
int dx = 0;
|
|
|
|
switch (icon) {
|
|
|
|
case TabIcon::NONE:
|
|
|
|
break;
|
|
|
|
case TabIcon::HOME:
|
|
|
|
{
|
|
|
|
theme->styles.tabHome()->paint(g,
|
|
|
|
gfx::Rect(
|
|
|
|
box.x,
|
|
|
|
box.y+dy,
|
|
|
|
box.x-dx,
|
|
|
|
box.h),
|
|
|
|
nullptr, state);
|
|
|
|
dx += theme->dimensions.tabsIconWidth();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2015-02-20 08:44:22 +08:00
|
|
|
|
2015-03-03 20:11:37 +08:00
|
|
|
// Tab with text + clipping the close button
|
|
|
|
if (box.w > 8*ui::guiscale()) {
|
|
|
|
theme->styles.tabText()->paint(g,
|
|
|
|
gfx::Rect(box.x+dx, box.y+dy, box.w-dx, box.h),
|
|
|
|
tab->text.c_str(), state);
|
|
|
|
}
|
2010-07-27 03:52:40 +08:00
|
|
|
}
|
|
|
|
|
2015-02-20 08:44:22 +08:00
|
|
|
// Tab bottom part
|
2015-02-18 03:03:00 +08:00
|
|
|
theme->styles.tabBottom()->paint(g,
|
|
|
|
gfx::Rect(box.x, box.y2(), box.w, getBounds().y2()-box.y2()),
|
|
|
|
nullptr, state);
|
2010-07-27 03:52:40 +08:00
|
|
|
|
2015-02-20 00:13:25 +08:00
|
|
|
// Close button
|
|
|
|
if (!closeBox.isEmpty()) {
|
|
|
|
skin::Style* style = theme->styles.tabCloseIcon();
|
2015-02-22 07:06:35 +08:00
|
|
|
|
|
|
|
if (m_delegate) {
|
|
|
|
if (tab->view)
|
2015-02-23 08:18:53 +08:00
|
|
|
tab->modified = m_delegate->onIsModified(this, tab->view);
|
2015-02-22 07:06:35 +08:00
|
|
|
|
|
|
|
if (tab->modified &&
|
|
|
|
(!hover || !m_hotCloseButton)) {
|
|
|
|
style = theme->styles.tabModifiedIcon();
|
|
|
|
}
|
2015-02-20 00:13:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
state = skin::Style::State();
|
|
|
|
if (hover && m_hotCloseButton) {
|
|
|
|
state += skin::Style::hover();
|
|
|
|
if (selected)
|
|
|
|
state += skin::Style::active();
|
|
|
|
if (m_clickedCloseButton)
|
|
|
|
state += skin::Style::clicked();
|
|
|
|
}
|
|
|
|
else if (selected)
|
|
|
|
state += skin::Style::active();
|
|
|
|
|
|
|
|
style->paint(g, closeBox, nullptr, state);
|
|
|
|
}
|
2010-07-27 03:52:40 +08:00
|
|
|
}
|
|
|
|
|
2015-02-25 07:10:22 +08:00
|
|
|
void Tabs::drawFiller(ui::Graphics* g, const gfx::Rect& box)
|
|
|
|
{
|
|
|
|
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
|
|
|
|
gfx::Rect rect = getClientBounds();
|
|
|
|
skin::Style::State state;
|
|
|
|
|
|
|
|
theme->styles.tabFiller()->paint(g,
|
|
|
|
gfx::Rect(box.x, box.y, rect.x2()-box.x, box.h), nullptr, state);
|
|
|
|
|
|
|
|
theme->styles.tabBottom()->paint(g,
|
|
|
|
gfx::Rect(box.x, box.y2(), rect.x2()-box.x, rect.y2()-box.y2()), nullptr, state);
|
|
|
|
}
|
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
Tabs::TabsListIterator Tabs::getTabIteratorByView(TabView* tabView)
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2015-02-15 22:45:23 +08:00
|
|
|
TabsListIterator it, end = m_list.end();
|
2008-03-23 02:43:56 +08:00
|
|
|
|
2015-02-15 22:45:23 +08:00
|
|
|
for (it = m_list.begin(); it != end; ++it) {
|
2013-01-21 05:40:37 +08:00
|
|
|
if ((*it)->view == tabView)
|
2012-02-12 22:33:06 +08:00
|
|
|
break;
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2012-02-12 22:33:06 +08:00
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
Tabs::Tab* Tabs::getTabByView(TabView* tabView)
|
2012-02-12 22:33:06 +08:00
|
|
|
{
|
2013-01-21 05:40:37 +08:00
|
|
|
TabsListIterator it = getTabIteratorByView(tabView);
|
2015-02-15 22:45:23 +08:00
|
|
|
if (it != m_list.end())
|
2012-02-12 22:33:06 +08:00
|
|
|
return *it;
|
|
|
|
else
|
|
|
|
return NULL;
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2015-02-22 07:06:35 +08:00
|
|
|
void Tabs::makeTabVisible(Tab* thisTab)
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2015-02-22 07:06:35 +08:00
|
|
|
updateTabs();
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
void Tabs::calculateHot()
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2015-02-25 07:10:22 +08:00
|
|
|
if (m_isDragging)
|
|
|
|
return;
|
|
|
|
|
2015-02-20 00:13:25 +08:00
|
|
|
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
|
2013-05-21 07:40:18 +08:00
|
|
|
gfx::Rect rect = getBounds();
|
2015-02-25 07:21:29 +08:00
|
|
|
gfx::Rect box(rect.x+m_border*guiscale(), rect.y, 0, rect.h-1);
|
2015-02-20 00:13:25 +08:00
|
|
|
gfx::Point mousePos = ui::get_mouse_position();
|
|
|
|
Tab* hot = NULL;
|
|
|
|
bool hotCloseButton = false;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
// For each tab
|
2015-02-15 22:45:23 +08:00
|
|
|
for (Tab* tab : m_list) {
|
2015-02-22 07:06:35 +08:00
|
|
|
box.w = tab->width;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2015-02-20 00:13:25 +08:00
|
|
|
if (box.contains(mousePos)) {
|
2007-11-28 22:19:36 +08:00
|
|
|
hot = tab;
|
2015-02-23 00:06:22 +08:00
|
|
|
hotCloseButton = getTabCloseButtonBounds(tab, box).contains(mousePos);
|
2007-11-28 22:19:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-05-21 07:40:18 +08:00
|
|
|
box.x += box.w;
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2015-02-20 00:13:25 +08:00
|
|
|
if (m_hot != hot ||
|
|
|
|
m_hotCloseButton != hotCloseButton) {
|
2010-06-14 04:51:22 +08:00
|
|
|
m_hot = hot;
|
2015-02-20 00:13:25 +08:00
|
|
|
m_hotCloseButton = hotCloseButton;
|
2010-06-14 04:51:22 +08:00
|
|
|
|
2011-04-03 01:57:40 +08:00
|
|
|
if (m_delegate)
|
2015-02-23 08:18:53 +08:00
|
|
|
m_delegate->onMouseOverTab(this, m_hot ? m_hot->view: NULL);
|
2010-06-14 04:51:22 +08:00
|
|
|
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
}
|
2010-06-14 04:51:22 +08:00
|
|
|
|
2015-02-23 00:06:22 +08:00
|
|
|
gfx::Rect Tabs::getTabCloseButtonBounds(Tab* tab, const gfx::Rect& box)
|
2010-07-20 11:59:52 +08:00
|
|
|
{
|
2015-02-20 00:13:25 +08:00
|
|
|
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
|
|
|
|
int iconW = theme->dimensions.tabsCloseIconWidth();
|
|
|
|
int iconH = theme->dimensions.tabsCloseIconHeight();
|
|
|
|
|
2015-02-23 00:06:22 +08:00
|
|
|
if (box.w-iconW > 32*ui::guiscale() || tab == m_selected)
|
2015-02-20 00:13:25 +08:00
|
|
|
return gfx::Rect(box.x2()-iconW, box.y+box.h/2-iconH/2, iconW, iconH);
|
|
|
|
else
|
|
|
|
return gfx::Rect();
|
2010-07-20 11:59:52 +08:00
|
|
|
}
|
|
|
|
|
2015-02-25 07:10:22 +08:00
|
|
|
void Tabs::resetOldPositions()
|
2010-07-20 11:59:52 +08:00
|
|
|
{
|
2015-02-22 07:06:35 +08:00
|
|
|
for (Tab* tab : m_list) {
|
|
|
|
tab->oldX = tab->x;
|
|
|
|
tab->oldWidth = tab->width;
|
|
|
|
}
|
2015-02-25 07:10:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Tabs::resetOldPositions(double t)
|
|
|
|
{
|
|
|
|
for (Tab* tab : m_list) {
|
|
|
|
tab->oldX = int(inbetween(tab->oldX, tab->x, t));
|
|
|
|
tab->oldWidth = int(inbetween(tab->oldWidth, tab->width, t));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Tabs::startAni(Ani ani, int T)
|
|
|
|
{
|
|
|
|
// Stop previous animation
|
|
|
|
if (m_ani != ANI_NONE)
|
|
|
|
stopAni();
|
2015-02-22 07:06:35 +08:00
|
|
|
|
2010-07-20 11:59:52 +08:00
|
|
|
m_ani = ani;
|
|
|
|
m_ani_t = 0;
|
2015-02-22 07:06:35 +08:00
|
|
|
m_ani_T = T;
|
2012-04-08 00:12:01 +08:00
|
|
|
m_timer.start();
|
2010-07-20 11:59:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Tabs::stopAni()
|
|
|
|
{
|
|
|
|
m_ani = ANI_NONE;
|
2012-04-08 00:12:01 +08:00
|
|
|
m_timer.stop();
|
2015-03-03 20:07:49 +08:00
|
|
|
|
|
|
|
if (m_list.empty())
|
|
|
|
getRoot()->layout();
|
2010-07-20 11:59:52 +08:00
|
|
|
}
|
|
|
|
|
2015-02-25 07:10:22 +08:00
|
|
|
void Tabs::startDrag()
|
|
|
|
{
|
|
|
|
ASSERT(m_selected);
|
|
|
|
|
|
|
|
updateTabs();
|
|
|
|
|
|
|
|
m_isDragging = true;
|
|
|
|
m_dragTabX = m_selected->x;
|
|
|
|
m_dragTabIndex = std::find(m_list.begin(), m_list.end(), m_selected) - m_list.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Tabs::stopDrag()
|
|
|
|
{
|
|
|
|
m_isDragging = false;
|
|
|
|
m_selected->oldX = m_selected->x;
|
|
|
|
m_selected->oldWidth = m_selected->width;
|
|
|
|
|
|
|
|
resetOldPositions(double(m_ani_t) / double(m_ani_T));
|
|
|
|
updateTabs();
|
|
|
|
startAni(ANI_REORDER_TABS, ANI_REORDER_TABS_TICKS);
|
|
|
|
}
|
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
} // namespace app
|