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
|
|
|
|
2014-04-18 01:39:29 +08:00
|
|
|
//#define CLOSE_BUTTON_IN_EACH_TAB
|
|
|
|
|
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"
|
|
|
|
#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>
|
|
|
|
|
2014-11-26 09:33:45 +08:00
|
|
|
#define ARROW_W (12*guiscale())
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2010-07-20 11:59:52 +08:00
|
|
|
#define ANI_ADDING_TAB_TICKS 5
|
|
|
|
#define ANI_REMOVING_TAB_TICKS 10
|
|
|
|
#define ANI_SMOOTH_SCROLL_TICKS 20
|
|
|
|
|
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;
|
|
|
|
|
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
|
|
|
|
2012-06-16 10:37:59 +08:00
|
|
|
class Tabs::ScrollButton : public Button
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ScrollButton(int direction, Tabs* tabs)
|
|
|
|
: Button("")
|
|
|
|
, m_direction(direction)
|
|
|
|
, m_tabs(tabs) {
|
|
|
|
}
|
|
|
|
|
|
|
|
int getDirection() const { return m_direction; }
|
|
|
|
|
|
|
|
protected:
|
2014-08-15 10:07:47 +08:00
|
|
|
bool onProcessMessage(Message* msg) override;
|
|
|
|
void onDisable() override;
|
2012-06-16 10:37:59 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
int m_direction;
|
|
|
|
Tabs* m_tabs;
|
|
|
|
};
|
|
|
|
|
2011-04-03 01:57:40 +08:00
|
|
|
Tabs::Tabs(TabsDelegate* delegate)
|
2010-06-14 04:51:22 +08:00
|
|
|
: Widget(tabs_type())
|
2011-04-03 01:57:40 +08:00
|
|
|
, m_delegate(delegate)
|
2012-07-06 12:06:00 +08:00
|
|
|
, m_timer(1000/60, this)
|
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;
|
|
|
|
m_selected = NULL;
|
2010-07-20 11:59:52 +08:00
|
|
|
m_scrollX = 0;
|
|
|
|
m_ani = ANI_NONE;
|
2010-07-27 03:52:40 +08:00
|
|
|
m_removedTab = NULL;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2012-06-16 10:37:59 +08:00
|
|
|
m_button_left = new ScrollButton(-1, this);
|
|
|
|
m_button_right = new ScrollButton(+1, this);
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
setup_mini_look(m_button_left);
|
|
|
|
setup_mini_look(m_button_right);
|
2010-08-24 04:41:19 +08:00
|
|
|
setup_bevels(m_button_left, 2, 0, 2, 0);
|
|
|
|
setup_bevels(m_button_right, 0, 2, 0, 2);
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2012-04-06 06:00:19 +08:00
|
|
|
m_button_left->setFocusStop(false);
|
|
|
|
m_button_right->setFocusStop(false);
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2011-03-07 03:15:05 +08:00
|
|
|
set_gfxicon_to_button(m_button_left,
|
2012-01-06 06:45:03 +08:00
|
|
|
PART_COMBOBOX_ARROW_LEFT,
|
|
|
|
PART_COMBOBOX_ARROW_LEFT_SELECTED,
|
|
|
|
PART_COMBOBOX_ARROW_LEFT_DISABLED, JI_CENTER | JI_MIDDLE);
|
2011-03-07 03:15:05 +08:00
|
|
|
|
|
|
|
set_gfxicon_to_button(m_button_right,
|
2012-01-06 06:45:03 +08:00
|
|
|
PART_COMBOBOX_ARROW_RIGHT,
|
|
|
|
PART_COMBOBOX_ARROW_RIGHT_SELECTED,
|
|
|
|
PART_COMBOBOX_ARROW_RIGHT_DISABLED, JI_CENTER | JI_MIDDLE);
|
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
|
2012-02-12 22:33:06 +08:00
|
|
|
TabsListIterator it, end = m_list_of_tabs.end();
|
2010-06-14 04:51:22 +08:00
|
|
|
for (it = m_list_of_tabs.begin(); it != end; ++it)
|
|
|
|
delete *it; // tab
|
|
|
|
m_list_of_tabs.clear();
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2012-01-06 06:45:03 +08:00
|
|
|
delete m_button_left; // widget
|
|
|
|
delete m_button_right; // widget
|
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
|
|
|
{
|
2013-01-21 05:40:37 +08:00
|
|
|
Tab* tab = new Tab(tabView);
|
|
|
|
calcTabWidth(tab);
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
m_list_of_tabs.push_back(tab);
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
// Update scroll (in the same position if we can
|
2010-07-20 11:59:52 +08:00
|
|
|
setScrollX(m_scrollX);
|
|
|
|
|
|
|
|
startAni(ANI_ADDING_TAB);
|
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)
|
|
|
|
m_hot = NULL;
|
|
|
|
|
|
|
|
if (m_selected == tab) {
|
|
|
|
selectNextTab();
|
|
|
|
if (m_selected == tab)
|
|
|
|
m_selected = NULL;
|
|
|
|
}
|
2009-11-22 22:14:06 +08:00
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
TabsListIterator it =
|
|
|
|
std::find(m_list_of_tabs.begin(), m_list_of_tabs.end(), tab);
|
2010-07-20 11:59:52 +08:00
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
ASSERT(it != m_list_of_tabs.end() && "Removing a tab that is not part of the Tabs widget");
|
2010-07-20 11:59:52 +08:00
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
it = m_list_of_tabs.erase(it);
|
2010-07-20 11:59:52 +08:00
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
// Width of the removed tab
|
|
|
|
if (m_removedTab) {
|
|
|
|
delete m_removedTab;
|
|
|
|
m_removedTab = NULL;
|
|
|
|
}
|
|
|
|
m_removedTab = tab;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
// Next tab in the list
|
|
|
|
if (it != m_list_of_tabs.end())
|
|
|
|
m_nextTabOfTheRemovedOne = *it;
|
|
|
|
else
|
|
|
|
m_nextTabOfTheRemovedOne = NULL;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
// Update scroll (in the same position if we can)
|
|
|
|
setScrollX(m_scrollX);
|
2010-07-20 11:59:52 +08:00
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
startAni(ANI_REMOVING_TAB);
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
void Tabs::updateTabsText()
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2013-01-21 05:40:37 +08:00
|
|
|
TabsListIterator it, end = m_list_of_tabs.end();
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
for (it = m_list_of_tabs.begin(); it != end; ++it) {
|
|
|
|
Tab* tab = *it;
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
// Change text of the tab
|
|
|
|
calcTabWidth(tab);
|
2007-11-28 22:19:36 +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);
|
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
Tab *tab = getTabByView(tabView);
|
2012-02-12 22:33:06 +08:00
|
|
|
if (tab != NULL)
|
|
|
|
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;
|
|
|
|
if (it != m_list_of_tabs.end()) {
|
|
|
|
// If we are at the end of the list, cycle to the first tab.
|
|
|
|
if (it == --m_list_of_tabs.end())
|
|
|
|
it = m_list_of_tabs.begin();
|
|
|
|
// Go to next tab.
|
|
|
|
else
|
|
|
|
++it;
|
|
|
|
|
|
|
|
if (it != currentTabIt) {
|
|
|
|
selectTabInternal(*it);
|
|
|
|
if (m_delegate)
|
2013-07-29 08:17:07 +08:00
|
|
|
m_delegate->clickTab(this, m_selected->view, kButtonLeft);
|
2012-02-12 22:33:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
if (it != m_list_of_tabs.end()) {
|
|
|
|
// If we are at the beginning of the list, cycle to the last tab.
|
|
|
|
if (it == m_list_of_tabs.begin())
|
|
|
|
it = --m_list_of_tabs.end();
|
|
|
|
// Go to previous tab.
|
|
|
|
else
|
|
|
|
--it;
|
|
|
|
|
|
|
|
if (it != currentTabIt) {
|
|
|
|
selectTabInternal(*it);
|
|
|
|
if (m_delegate)
|
2013-07-29 08:17:07 +08:00
|
|
|
m_delegate->clickTab(this, m_selected->view, kButtonLeft);
|
2012-02-12 22:33:06 +08:00
|
|
|
}
|
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:
|
|
|
|
case kMouseMoveMessage:
|
2010-06-14 04:51:22 +08:00
|
|
|
calculateHot();
|
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:
|
2014-08-09 22:13:51 +08:00
|
|
|
case kMouseUpMessage:
|
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);
|
|
|
|
|
2012-01-06 06:45:03 +08:00
|
|
|
if (m_selected != m_hot) {
|
|
|
|
m_selected = m_hot;
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
|
2014-08-09 22:13:51 +08:00
|
|
|
// Left button is processed in mouse down message, right
|
|
|
|
// button is processed in mouse up.
|
|
|
|
if (m_selected && m_delegate &&
|
|
|
|
((mouseMsg->left() && msg->type() == kMouseDownMessage) ||
|
|
|
|
(!mouseMsg->left() && msg->type() == kMouseUpMessage))) {
|
|
|
|
m_delegate->clickTab(this, m_selected->view, mouseMsg->buttons());
|
|
|
|
}
|
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 =
|
|
|
|
(static_cast<MouseMessage*>(msg)->wheelDelta().x -
|
|
|
|
static_cast<MouseMessage*>(msg)->wheelDelta().y) * getBounds().w/6;
|
2010-07-20 11:59:52 +08:00
|
|
|
|
|
|
|
m_begScrollX = m_scrollX;
|
|
|
|
if (m_ani != ANI_SMOOTH_SCROLL)
|
2014-04-29 09:02:56 +08:00
|
|
|
m_endScrollX = m_scrollX + dz;
|
2010-07-20 11:59:52 +08:00
|
|
|
else
|
2014-04-29 09:02:56 +08:00
|
|
|
m_endScrollX += dz;
|
2010-07-20 11:59:52 +08:00
|
|
|
|
|
|
|
// Limit endScrollX position (to improve animation ending to the correct position)
|
|
|
|
{
|
2012-01-06 06:45:03 +08:00
|
|
|
int max_x = getMaxScrollX();
|
|
|
|
m_endScrollX = MID(0, m_endScrollX, max_x);
|
2010-07-20 11:59:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
startAni(ANI_SMOOTH_SCROLL);
|
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: {
|
2010-07-20 11:59:52 +08:00
|
|
|
switch (m_ani) {
|
2012-01-06 06:45:03 +08:00
|
|
|
case ANI_NONE:
|
|
|
|
// Do nothing
|
|
|
|
break;
|
|
|
|
case ANI_SCROLL: {
|
2012-06-16 10:37:59 +08:00
|
|
|
ScrollButton* button = dynamic_cast<ScrollButton*>(getManager()->getCapture());
|
|
|
|
if (button != NULL)
|
2013-07-29 08:17:07 +08:00
|
|
|
setScrollX(m_scrollX + button->getDirection()*8*static_cast<TimerMessage*>(msg)->count());
|
2012-01-06 06:45:03 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ANI_SMOOTH_SCROLL: {
|
|
|
|
if (m_ani_t == ANI_SMOOTH_SCROLL_TICKS) {
|
|
|
|
stopAni();
|
|
|
|
setScrollX(m_endScrollX);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Lineal
|
|
|
|
//setScrollX(m_begScrollX + m_endScrollX - m_begScrollX) * m_ani_t / 10);
|
|
|
|
|
|
|
|
// Exponential
|
|
|
|
setScrollX(m_begScrollX +
|
2015-02-15 09:37:56 +08:00
|
|
|
int((m_endScrollX - m_begScrollX) * (1.0-std::exp(-10.0 * m_ani_t / (double)ANI_SMOOTH_SCROLL_TICKS))));
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ANI_ADDING_TAB: {
|
|
|
|
if (m_ani_t == ANI_ADDING_TAB_TICKS)
|
|
|
|
stopAni();
|
|
|
|
invalidate();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ANI_REMOVING_TAB: {
|
|
|
|
if (m_ani_t == ANI_REMOVING_TAB_TICKS)
|
|
|
|
stopAni();
|
|
|
|
invalidate();
|
|
|
|
break;
|
|
|
|
}
|
2010-07-20 11:59:52 +08:00
|
|
|
}
|
|
|
|
++m_ani_t;
|
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();
|
2014-04-18 01:47:28 +08:00
|
|
|
gfx::Rect box(rect.x-m_scrollX, rect.y,
|
2014-11-26 09:33:45 +08:00
|
|
|
2*guiscale(),
|
2014-06-23 05:53:14 +08:00
|
|
|
m_list_of_tabs.empty() ? 0: theme->get_part(PART_TAB_FILLER)->height());
|
2013-05-21 07:40:18 +08:00
|
|
|
|
2014-03-21 11:28:01 +08:00
|
|
|
g->fillRect(theme->getColorById(kWindowFaceColorId), g->getClipBounds());
|
2013-05-21 07:40:18 +08:00
|
|
|
|
|
|
|
theme->draw_part_as_hline(g, box, PART_TAB_FILLER);
|
|
|
|
theme->draw_part_as_hline(g, gfx::Rect(box.x, box.y2(), box.w, rect.y2()-box.y2()), PART_TAB_BOTTOM_NORMAL);
|
|
|
|
|
|
|
|
box.x = box.x2();
|
|
|
|
|
|
|
|
// For each tab...
|
|
|
|
TabsListIterator it, end = m_list_of_tabs.end();
|
|
|
|
|
|
|
|
for (it = m_list_of_tabs.begin(); it != end; ++it) {
|
|
|
|
Tab* tab = *it;
|
|
|
|
|
|
|
|
box.w = tab->width;
|
|
|
|
|
|
|
|
int x_delta = 0;
|
|
|
|
int y_delta = 0;
|
|
|
|
|
|
|
|
// Y-delta for animating tabs (intros and outros)
|
|
|
|
if (m_ani == ANI_ADDING_TAB && m_selected == tab) {
|
|
|
|
y_delta = box.h * (ANI_ADDING_TAB_TICKS - m_ani_t) / ANI_ADDING_TAB_TICKS;
|
|
|
|
}
|
|
|
|
else if (m_ani == ANI_REMOVING_TAB && m_nextTabOfTheRemovedOne == tab) {
|
2015-02-15 09:37:56 +08:00
|
|
|
x_delta += m_removedTab->width
|
|
|
|
- int(double(m_removedTab->width)*(1.0-std::exp(-10.0 * m_ani_t / (double)ANI_REMOVING_TAB_TICKS)));
|
2013-05-21 07:40:18 +08:00
|
|
|
x_delta = MID(0, x_delta, m_removedTab->width);
|
|
|
|
|
|
|
|
// Draw deleted tab
|
|
|
|
if (m_removedTab) {
|
|
|
|
gfx::Rect box2(box.x, box.y, x_delta, box.h);
|
|
|
|
drawTab(g, box2, m_removedTab, 0, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
box.x += x_delta;
|
|
|
|
drawTab(g, box, tab, y_delta, (tab == m_selected));
|
|
|
|
|
|
|
|
box.x = box.x2();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_ani == ANI_REMOVING_TAB && m_nextTabOfTheRemovedOne == NULL) {
|
|
|
|
// Draw deleted tab
|
|
|
|
if (m_removedTab) {
|
2015-02-15 09:37:56 +08:00
|
|
|
int x_delta = m_removedTab->width
|
|
|
|
- int(double(m_removedTab->width)*(1.0-std::exp(-10.0 * m_ani_t / (double)ANI_REMOVING_TAB_TICKS)));
|
2013-05-21 07:40:18 +08:00
|
|
|
x_delta = MID(0, x_delta, m_removedTab->width);
|
|
|
|
|
|
|
|
gfx::Rect box2(box.x, box.y, x_delta, box.h);
|
|
|
|
drawTab(g, box2, m_removedTab, 0, false);
|
|
|
|
|
|
|
|
box.x += x_delta;
|
|
|
|
box.w = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fill the gap to the right-side
|
|
|
|
if (box.x < rect.x2()) {
|
|
|
|
theme->draw_part_as_hline(g, gfx::Rect(box.x, box.y, rect.x2()-box.x, box.h), PART_TAB_FILLER);
|
|
|
|
theme->draw_part_as_hline(g, gfx::Rect(box.x, box.y2(), rect.x2()-box.x, rect.y2()-box.y2()), PART_TAB_BOTTOM_NORMAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-12 04:56:27 +08:00
|
|
|
void Tabs::onResize(ResizeEvent& ev)
|
|
|
|
{
|
|
|
|
setBoundsQuietly(ev.getBounds());
|
|
|
|
setScrollX(m_scrollX);
|
|
|
|
}
|
|
|
|
|
2012-09-27 05:34:52 +08:00
|
|
|
void Tabs::onPreferredSize(PreferredSizeEvent& ev)
|
|
|
|
{
|
|
|
|
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
|
2014-06-23 05:53:14 +08:00
|
|
|
gfx::Size reqsize(0, theme->get_part(PART_TAB_BOTTOM_NORMAL)->height());
|
2012-09-27 05:34:52 +08:00
|
|
|
|
2014-04-18 01:47:28 +08:00
|
|
|
if (!m_list_of_tabs.empty()) {
|
2014-06-23 05:53:14 +08:00
|
|
|
reqsize.h += theme->get_part(PART_TAB_FILLER)->height();
|
2014-04-18 01:47:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ev.setPreferredSize(reqsize);
|
2012-09-27 05:34:52 +08:00
|
|
|
}
|
|
|
|
|
2012-06-16 10:37:59 +08:00
|
|
|
void Tabs::onInitTheme(InitThemeEvent& ev)
|
|
|
|
{
|
|
|
|
Widget::onInitTheme(ev);
|
|
|
|
|
2013-01-07 01:45:43 +08:00
|
|
|
SkinTheme* theme = static_cast<SkinTheme*>(ev.getTheme());
|
2012-06-16 10:37:59 +08:00
|
|
|
|
2013-01-07 01:45:43 +08:00
|
|
|
m_button_left->setBgColor(theme->getColor(ThemeColor::TabSelectedFace));
|
|
|
|
m_button_right->setBgColor(theme->getColor(ThemeColor::TabSelectedFace));
|
2012-06-16 10:37:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Tabs::onSetText()
|
|
|
|
{
|
|
|
|
TabsListIterator it, end = m_list_of_tabs.end();
|
|
|
|
|
|
|
|
for (it = m_list_of_tabs.begin(); it != end; ++it) {
|
|
|
|
Tab* tab = *it;
|
2013-01-21 05:40:37 +08:00
|
|
|
calcTabWidth(tab);
|
2012-06-16 10:37:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-12 22:33:06 +08:00
|
|
|
void Tabs::selectTabInternal(Tab* tab)
|
|
|
|
{
|
|
|
|
m_selected = tab;
|
|
|
|
makeTabVisible(tab);
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
|
2013-05-21 07:40:18 +08:00
|
|
|
void Tabs::drawTab(Graphics* g, const gfx::Rect& box, Tab* tab, int y_delta, bool selected)
|
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;
|
|
|
|
|
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;
|
2010-07-27 03:52:40 +08:00
|
|
|
|
|
|
|
// Selected
|
|
|
|
if (selected) {
|
2013-01-07 01:45:43 +08:00
|
|
|
text_color = theme->getColor(ThemeColor::TabSelectedText);
|
|
|
|
face_color = theme->getColor(ThemeColor::TabSelectedFace);
|
2010-07-27 03:52:40 +08:00
|
|
|
}
|
|
|
|
// Non-selected
|
|
|
|
else {
|
2013-01-07 01:45:43 +08:00
|
|
|
text_color = theme->getColor(ThemeColor::TabNormalText);
|
|
|
|
face_color = theme->getColor(ThemeColor::TabNormalFace);
|
2010-07-27 03:52:40 +08:00
|
|
|
}
|
|
|
|
|
2013-05-21 07:40:18 +08:00
|
|
|
if (box.w > 2) {
|
|
|
|
theme->draw_bounds_nw(g,
|
|
|
|
gfx::Rect(box.x, box.y+y_delta, box.w, box.h),
|
2012-01-06 06:45:03 +08:00
|
|
|
(selected) ? PART_TAB_SELECTED_NW:
|
2013-01-07 01:45:43 +08:00
|
|
|
PART_TAB_NORMAL_NW,
|
|
|
|
face_color);
|
|
|
|
|
2014-06-29 03:10:39 +08:00
|
|
|
g->drawString(tab->text, text_color, gfx::ColorNone,
|
2014-06-23 05:53:14 +08:00
|
|
|
gfx::Point(
|
2014-11-26 09:33:45 +08:00
|
|
|
box.x + 4*guiscale(),
|
2014-06-23 05:53:14 +08:00
|
|
|
box.y + box.h/2 - getFont()->height()/2+1 + y_delta));
|
2010-07-27 03:52:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (selected) {
|
2014-06-23 05:53:14 +08:00
|
|
|
theme->draw_bounds_nw(g,
|
|
|
|
gfx::Rect(box.x, box.y2(), box.w, getBounds().y2()-box.y2()),
|
|
|
|
PART_TAB_BOTTOM_SELECTED_NW,
|
|
|
|
theme->getColor(ThemeColor::TabSelectedFace));
|
2010-07-27 03:52:40 +08:00
|
|
|
}
|
|
|
|
else {
|
2014-06-23 05:53:14 +08:00
|
|
|
theme->draw_part_as_hline(g,
|
|
|
|
gfx::Rect(box.x, box.y2(), box.w, getBounds().y2()-box.y2()),
|
|
|
|
PART_TAB_BOTTOM_NORMAL);
|
2010-07-27 03:52:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CLOSE_BUTTON_IN_EACH_TAB
|
2014-06-23 05:53:14 +08:00
|
|
|
she::Surface* close_icon = theme->get_part(PART_WINDOW_CLOSE_BUTTON_NORMAL);
|
|
|
|
g->drawRgbaSurface(close_icon,
|
2014-11-26 09:33:45 +08:00
|
|
|
box.x2() - 4*guiscale() - close_icon->width(),
|
|
|
|
box.y + box.h/2 - close_icon->height()/2+1 * guiscale());
|
2010-07-27 03:52:40 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
Tabs::TabsListIterator Tabs::getTabIteratorByView(TabView* tabView)
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2012-02-12 22:33:06 +08:00
|
|
|
TabsListIterator it, end = m_list_of_tabs.end();
|
2008-03-23 02:43:56 +08:00
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
for (it = m_list_of_tabs.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);
|
2012-02-12 22:33:06 +08:00
|
|
|
if (it != m_list_of_tabs.end())
|
|
|
|
return *it;
|
|
|
|
else
|
|
|
|
return NULL;
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
int Tabs::getMaxScrollX()
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2012-02-12 22:33:06 +08:00
|
|
|
TabsListIterator it, end = m_list_of_tabs.end();
|
2007-11-28 22:19:36 +08:00
|
|
|
int x = 0;
|
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
for (it = m_list_of_tabs.begin(); it != end; ++it) {
|
|
|
|
Tab* tab = *it;
|
2007-11-28 22:19:36 +08:00
|
|
|
x += tab->width;
|
|
|
|
}
|
|
|
|
|
2013-10-26 23:50:55 +08:00
|
|
|
x -= getBounds().w;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
|
|
|
if (x < 0)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return x + ARROW_W*2;
|
|
|
|
}
|
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
void Tabs::makeTabVisible(Tab* make_visible_this_tab)
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
|
|
|
int x = 0;
|
2010-06-14 04:51:22 +08:00
|
|
|
int extra_x = getMaxScrollX() > 0 ? ARROW_W*2: 0;
|
2012-02-12 22:33:06 +08:00
|
|
|
TabsListIterator it, end = m_list_of_tabs.end();
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
for (it = m_list_of_tabs.begin(); it != end; ++it) {
|
|
|
|
Tab* tab = *it;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
|
|
|
if (tab == make_visible_this_tab) {
|
2010-07-20 11:59:52 +08:00
|
|
|
if (x - m_scrollX < 0) {
|
2012-01-06 06:45:03 +08:00
|
|
|
setScrollX(x);
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
2013-10-26 23:50:55 +08:00
|
|
|
else if (x + tab->width - m_scrollX > getBounds().w - extra_x) {
|
|
|
|
setScrollX(x + tab->width - getBounds().w + extra_x);
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
x += tab->width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
void Tabs::setScrollX(int scroll_x)
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2010-06-14 04:51:22 +08:00
|
|
|
int max_x = getMaxScrollX();
|
2007-11-28 22:19:36 +08:00
|
|
|
|
|
|
|
scroll_x = MID(0, scroll_x, max_x);
|
2010-07-20 11:59:52 +08:00
|
|
|
if (m_scrollX != scroll_x) {
|
|
|
|
m_scrollX = scroll_x;
|
2010-06-14 04:51:22 +08:00
|
|
|
calculateHot();
|
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
|
|
|
// We need scroll buttons?
|
2007-11-28 22:19:36 +08:00
|
|
|
if (max_x > 0) {
|
2010-06-14 04:51:22 +08:00
|
|
|
// Add childs
|
|
|
|
if (!HAS_ARROWS(this)) {
|
2011-03-30 08:35:17 +08:00
|
|
|
addChild(m_button_left);
|
|
|
|
addChild(m_button_right);
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* disable/enable buttons */
|
2010-07-20 11:59:52 +08:00
|
|
|
m_button_left->setEnabled(m_scrollX > 0);
|
|
|
|
m_button_right->setEnabled(m_scrollX < max_x);
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2013-05-12 04:56:27 +08:00
|
|
|
// Setup the position of each button
|
|
|
|
gfx::Rect rect = getBounds();
|
|
|
|
gfx::Rect box(rect.x2()-ARROW_W*2, rect.y, ARROW_W, rect.h-2);
|
|
|
|
m_button_left->setBounds(box);
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2013-05-12 04:56:27 +08:00
|
|
|
box.x += ARROW_W;
|
|
|
|
m_button_right->setBounds(box);
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
2010-06-14 04:51:22 +08:00
|
|
|
// Remove buttons
|
|
|
|
else if (HAS_ARROWS(this)) {
|
2011-03-30 08:35:17 +08:00
|
|
|
removeChild(m_button_left);
|
|
|
|
removeChild(m_button_right);
|
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
|
|
|
void Tabs::calculateHot()
|
2007-11-28 22:19:36 +08:00
|
|
|
{
|
2013-05-21 07:40:18 +08:00
|
|
|
gfx::Rect rect = getBounds();
|
|
|
|
gfx::Rect box(rect.x-m_scrollX, rect.y, 0, rect.h-1);
|
2007-11-28 22:19:36 +08:00
|
|
|
Tab *hot = NULL;
|
2012-02-12 22:33:06 +08:00
|
|
|
TabsListIterator it, end = m_list_of_tabs.end();
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
// For each tab
|
|
|
|
for (it = m_list_of_tabs.begin(); it != end; ++it) {
|
|
|
|
Tab* tab = *it;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2013-05-21 07:40:18 +08:00
|
|
|
box.w = tab->width;
|
2007-11-28 22:19:36 +08:00
|
|
|
|
2014-11-26 09:10:28 +08:00
|
|
|
if (box.contains(ui::get_mouse_position())) {
|
2007-11-28 22:19:36 +08:00
|
|
|
hot = tab;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-05-21 07:40:18 +08:00
|
|
|
box.x += box.w;
|
2007-11-28 22:19:36 +08:00
|
|
|
}
|
|
|
|
|
2010-06-14 04:51:22 +08:00
|
|
|
if (m_hot != hot) {
|
|
|
|
m_hot = hot;
|
|
|
|
|
2011-04-03 01:57:40 +08:00
|
|
|
if (m_delegate)
|
2013-01-21 05:40:37 +08:00
|
|
|
m_delegate->mouseOverTab(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
|
|
|
|
2013-01-21 05:40:37 +08:00
|
|
|
void Tabs::calcTabWidth(Tab* tab)
|
2010-07-20 10:56:12 +08:00
|
|
|
{
|
2013-01-21 05:40:37 +08:00
|
|
|
// Cache current tab text
|
|
|
|
tab->text = tab->view->getTabText();
|
|
|
|
|
2014-11-26 09:33:45 +08:00
|
|
|
int border = 4*guiscale();
|
2010-07-20 10:56:12 +08:00
|
|
|
#ifdef CLOSE_BUTTON_IN_EACH_TAB
|
2014-04-18 01:39:29 +08:00
|
|
|
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
|
2014-06-23 05:53:14 +08:00
|
|
|
int close_icon_w = theme->get_part(PART_WINDOW_CLOSE_BUTTON_NORMAL)->width();
|
|
|
|
tab->width = (border + getFont()->textLength(tab->text.c_str()) + border + close_icon_w + border);
|
2010-07-20 10:56:12 +08:00
|
|
|
#else
|
2014-06-23 05:53:14 +08:00
|
|
|
tab->width = (border + getFont()->textLength(tab->text.c_str()) + border);
|
2010-07-20 10:56:12 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2010-07-20 11:59:52 +08:00
|
|
|
void Tabs::startScrolling()
|
|
|
|
{
|
|
|
|
startAni(ANI_SCROLL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Tabs::stopScrolling()
|
|
|
|
{
|
|
|
|
stopAni();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Tabs::startAni(Ani ani)
|
|
|
|
{
|
|
|
|
// Stop previous animation
|
|
|
|
if (m_ani != ANI_NONE)
|
|
|
|
stopAni();
|
|
|
|
|
|
|
|
m_ani = ani;
|
|
|
|
m_ani_t = 0;
|
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();
|
2010-07-20 11:59:52 +08:00
|
|
|
}
|
|
|
|
|
2012-06-16 10:37:59 +08:00
|
|
|
bool Tabs::ScrollButton::onProcessMessage(Message* msg)
|
2010-06-14 04:51:22 +08:00
|
|
|
{
|
2013-07-29 08:17:07 +08:00
|
|
|
switch (msg->type()) {
|
2010-06-14 04:51:22 +08:00
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseDownMessage:
|
2012-06-16 10:37:59 +08:00
|
|
|
captureMouse();
|
|
|
|
m_tabs->startScrolling();
|
|
|
|
return true;
|
2010-06-14 04:51:22 +08:00
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseUpMessage:
|
2012-06-16 10:37:59 +08:00
|
|
|
if (hasCapture())
|
|
|
|
releaseMouse();
|
|
|
|
|
|
|
|
m_tabs->stopScrolling();
|
|
|
|
return true;
|
2010-06-14 04:51:22 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-06-16 10:37:59 +08:00
|
|
|
return Button::onProcessMessage(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Tabs::ScrollButton::onDisable()
|
|
|
|
{
|
|
|
|
Button::onDisable();
|
|
|
|
|
|
|
|
if (hasCapture())
|
|
|
|
releaseMouse();
|
|
|
|
|
|
|
|
if (isSelected()) {
|
|
|
|
m_tabs->stopScrolling();
|
|
|
|
setSelected(false);
|
|
|
|
}
|
2010-06-14 04:51:22 +08:00
|
|
|
}
|
2013-08-06 08:20:19 +08:00
|
|
|
|
|
|
|
} // namespace app
|