Add close/modified icon to tabs

This commit is contained in:
David Capello 2015-02-19 13:13:25 -03:00
parent 13611eb23e
commit f4e01345c6
12 changed files with 256 additions and 217 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -6,6 +6,9 @@
<dimensions>
<dim id="tabs_height" value="17" />
<dim id="tabs_empty_height" value="5" />
<dim id="tabs_width" value="80" />
<dim id="tabs_close_icon_width" value="14" />
<dim id="tabs_close_icon_height" value="12" />
</dimensions>
<colors>
@ -224,8 +227,14 @@
<part id="tab_bottom_active" x="16" y="124" w1="4" w2="7" w3="5" h1="2" h2="1" h3="2" />
<part id="tab_bottom_normal" x="2" y="124" w="12" h="5" />
<part id="tab_filler" x="0" y="112" w="2" h="12" />
<part id="editor_normal" x="32" y="112" w1="3" w2="10" w3="3" h1="3" h2="10" h3="3" />
<part id="editor_selected" x="48" y="112" w1="3" w2="10" w3="3" h1="3" h2="10" h3="3" />
<part id="tab_modified_icon_normal" x="32" y="112" w="5" h="5" />
<part id="tab_modified_icon_active" x="32" y="117" w="5" h="5" />
<part id="tab_close_icon_normal" x="37" y="112" w="5" h="5" />
<part id="tab_close_icon_active" x="37" y="117" w="5" h="5" />
<part id="tab_icon_bg_active" x="42" y="112" w="14" h="12" />
<part id="tab_icon_bg_hover" x="56" y="112" w="14" h="12" />
<part id="editor_normal" x="40" y="96" w1="3" w2="10" w3="3" h1="3" h2="10" h3="3" />
<part id="editor_selected" x="56" y="96" w1="3" w2="10" w3="3" h1="3" h2="10" h3="3" />
<part id="colorbar_0" x="0" y="192" w1="5" w2="6" w3="5" h1="5" h2="6" h3="5" />
<part id="colorbar_1" x="16" y="192" w1="5" w2="6" w3="5" h1="5" h2="6" h3="5" />
<part id="colorbar_2" x="0" y="208" w1="5" w2="6" w3="5" h1="5" h2="6" h3="5" />
@ -617,16 +626,59 @@
<background part="tab_active" color="tab_active_face" />
<text color="tab_active_text" />
</style>
<!-- tab_bottom -->
<style id="tab_bottom">
<background part="tab_bottom_normal" color="tab_normal_face" repeat="repeat-x" />
</style>
<style id="tab_bottom:active">
<background part="tab_bottom_active" color="tab_active_face" />
</style>
<!-- tab_filler -->
<style id="tab_filler">
<background part="tab_filler" color="window_face" repeat="repeat-x" />
</style>
<!-- tab_icon -->
<style id="tab_icon">
<icon align="left" valign="middle" x="3" />
</style>
<style id="tab_icon:hover">
<background part="tab_icon_bg_hover" />
</style>
<style id="tab_icon:clicked">
<background part="tab_icon_bg_active" />
</style>
<!-- tab_close_icon -->
<style id="tab_close_icon" base="tab_icon">
<icon part="tab_close_icon_normal" />
</style>
<style id="tab_close_icon:hover">
<icon part="tab_close_icon_normal" />
</style>
<style id="tab_close_icon:active">
<icon part="tab_close_icon_active" />
</style>
<style id="tab_close_icon:clicked">
<icon part="tab_close_icon_normal" />
</style>
<!-- tab_modified_icon -->
<style id="tab_modified_icon" base="tab_icon">
<icon part="tab_modified_icon_normal" />
</style>
<style id="tab_modified_icon:hover">
<icon part="tab_modified_icon_normal" />
</style>
<style id="tab_modified_icon:active">
<icon part="tab_modified_icon_active" />
</style>
<style id="tab_modified_icon:clicked">
<icon part="tab_modified_icon_normal" />
</style>
</stylesheet>
</skin>

View File

@ -191,13 +191,7 @@ void DocumentView::getDocumentLocation(DocumentLocation* location) const
std::string DocumentView::getTabText()
{
std::string str = m_document->name();
// Add an asterisk if the document is modified.
if (m_document->isModified())
str += "*";
return str;
return m_document->name();
}
WorkspaceView* DocumentView::cloneWorkspaceView()

View File

@ -294,6 +294,22 @@ void MainWindow::clickTab(Tabs* tabs, TabView* tabView, ui::MouseButtons buttons
}
}
void MainWindow::clickClose(Tabs* tabs, TabView* tabView)
{
DocumentView* docView = dynamic_cast<DocumentView*>(tabView);
if (!docView)
return;
UIContext* context = UIContext::instance();
context->setActiveView(docView);
context->updateFlags();
Command* close_file_cmd =
CommandsModule::instance()->getCommandByName(CommandId::CloseFile);
context->executeCommand(close_file_cmd, NULL);
}
void MainWindow::mouseOverTab(Tabs* tabs, TabView* tabView)
{
// Note: tabView can be NULL
@ -307,4 +323,15 @@ void MainWindow::mouseOverTab(Tabs* tabs, TabView* tabView)
}
}
bool MainWindow::isModified(Tabs* tabs, TabView* tabView)
{
if (DocumentView* docView = dynamic_cast<DocumentView*>(tabView)) {
Document* document = docView->getDocument();
return document->isModified();
}
else {
return false;
}
}
} // namespace app

View File

@ -61,8 +61,10 @@ namespace app {
void popTimeline();
// TabsDelegate implementation.
void clickTab(Tabs* tabs, TabView* tabView, ui::MouseButtons buttons);
void mouseOverTab(Tabs* tabs, TabView* tabView);
void clickTab(Tabs* tabs, TabView* tabView, ui::MouseButtons buttons) override;
void clickClose(Tabs* tabs, TabView* tabView) override;
void mouseOverTab(Tabs* tabs, TabView* tabView) override;
bool isModified(Tabs* tabs, TabView* tabView) override;
protected:
bool onProcessMessage(ui::Message* msg) override;

View File

@ -583,20 +583,26 @@ void SkinTheme::onRegenerate()
else if (ruleName == "icon") {
if (align) (*style)[StyleSheet::iconAlignRule()] = css::Value(align);
if (part_id) (*style)[StyleSheet::iconPartRule()] = css::Value(part_id);
const char* x = xmlRule->Attribute("x");
const char* y = xmlRule->Attribute("y");
if (x) (*style)[StyleSheet::iconXRule()] = css::Value(strtol(x, NULL, 10));
if (y) (*style)[StyleSheet::iconYRule()] = css::Value(strtol(y, NULL, 10));
}
else if (ruleName == "text") {
if (color_id) (*style)[StyleSheet::textColorRule()] = css::Value(color_id);
if (align) (*style)[StyleSheet::textAlignRule()] = css::Value(align);
const char* padding_left = xmlRule->Attribute("padding-left");
const char* padding_top = xmlRule->Attribute("padding-top");
const char* padding_right = xmlRule->Attribute("padding-right");
const char* padding_bottom = xmlRule->Attribute("padding-bottom");
const char* l = xmlRule->Attribute("padding-left");
const char* t = xmlRule->Attribute("padding-top");
const char* r = xmlRule->Attribute("padding-right");
const char* b = xmlRule->Attribute("padding-bottom");
if (padding_left) (*style)[StyleSheet::paddingLeftRule()] = css::Value(strtol(padding_left, NULL, 10));
if (padding_top) (*style)[StyleSheet::paddingTopRule()] = css::Value(strtol(padding_top, NULL, 10));
if (padding_right) (*style)[StyleSheet::paddingRightRule()] = css::Value(strtol(padding_right, NULL, 10));
if (padding_bottom) (*style)[StyleSheet::paddingBottomRule()] = css::Value(strtol(padding_bottom, NULL, 10));
if (l) (*style)[StyleSheet::paddingLeftRule()] = css::Value(strtol(l, NULL, 10));
if (t) (*style)[StyleSheet::paddingTopRule()] = css::Value(strtol(t, NULL, 10));
if (r) (*style)[StyleSheet::paddingRightRule()] = css::Value(strtol(r, NULL, 10));
if (b) (*style)[StyleSheet::paddingBottomRule()] = css::Value(strtol(b, NULL, 10));
}
xmlRule = xmlRule->NextSiblingElement();

View File

@ -107,6 +107,9 @@ void IconRule::onPaint(ui::Graphics* g, const gfx::Rect& bounds, const char* tex
else
y = bounds.y;
x += m_x;
y += m_y;
g->drawRgbaSurface(bmp, x, y);
}
@ -120,6 +123,8 @@ Rules::Rules(const css::Query& query) :
css::Value backgroundRepeat = query[StyleSheet::backgroundRepeatRule()];
css::Value iconAlign = query[StyleSheet::iconAlignRule()];
css::Value iconPart = query[StyleSheet::iconPartRule()];
css::Value iconX = query[StyleSheet::iconXRule()];
css::Value iconY = query[StyleSheet::iconYRule()];
css::Value textAlign = query[StyleSheet::textAlignRule()];
css::Value textColor = query[StyleSheet::textColorRule()];
css::Value paddingLeft = query[StyleSheet::paddingLeftRule()];
@ -138,10 +143,14 @@ Rules::Rules(const css::Query& query) :
}
if (iconAlign != none
|| iconPart != none) {
|| iconPart != none
|| iconX != none
|| iconY != none) {
m_icon = new IconRule();
m_icon->setAlign((int)iconAlign.number());
m_icon->setPart(StyleSheet::convertPart(iconPart));
m_icon->setX((int)iconX.number()*ui::guiscale());
m_icon->setY((int)iconY.number()*ui::guiscale());
}
if (textAlign != none

View File

@ -85,6 +85,8 @@ namespace app {
void setAlign(int align) { m_align = align; }
void setPart(const SkinPartPtr& part) { m_part = part; }
void setX(int x) { m_x = x; }
void setY(int y) { m_y = y; }
SkinPartPtr getPart() { return m_part; }
@ -94,6 +96,7 @@ namespace app {
private:
int m_align;
SkinPartPtr m_part;
int m_x, m_y;
};
class Rules {

View File

@ -26,6 +26,8 @@ css::Rule StyleSheet::m_backgroundPartRule("background-part");
css::Rule StyleSheet::m_backgroundRepeatRule("background-repeat");
css::Rule StyleSheet::m_iconAlignRule("icon-align");
css::Rule StyleSheet::m_iconPartRule("icon-part");
css::Rule StyleSheet::m_iconXRule("icon-x");
css::Rule StyleSheet::m_iconYRule("icon-y");
css::Rule StyleSheet::m_textAlignRule("text-align");
css::Rule StyleSheet::m_textColorRule("text-color");
css::Rule StyleSheet::m_paddingLeftRule("padding-left");
@ -41,6 +43,8 @@ StyleSheet::StyleSheet()
m_sheet->addRule(&m_backgroundRepeatRule);
m_sheet->addRule(&m_iconAlignRule);
m_sheet->addRule(&m_iconPartRule);
m_sheet->addRule(&m_iconXRule);
m_sheet->addRule(&m_iconYRule);
m_sheet->addRule(&m_textAlignRule);
m_sheet->addRule(&m_textColorRule);
m_sheet->addRule(&m_paddingLeftRule);

View File

@ -41,6 +41,8 @@ namespace app {
static css::Rule& backgroundRepeatRule() { return m_backgroundRepeatRule; }
static css::Rule& iconAlignRule() { return m_iconAlignRule; }
static css::Rule& iconPartRule() { return m_iconPartRule; }
static css::Rule& iconXRule() { return m_iconXRule; }
static css::Rule& iconYRule() { return m_iconYRule; }
static css::Rule& textAlignRule() { return m_textAlignRule; }
static css::Rule& textColorRule() { return m_textColorRule; }
static css::Rule& paddingLeftRule() { return m_paddingLeftRule; }
@ -65,6 +67,8 @@ namespace app {
static css::Rule m_backgroundRepeatRule;
static css::Rule m_iconAlignRule;
static css::Rule m_iconPartRule;
static css::Rule m_iconXRule;
static css::Rule m_iconYRule;
static css::Rule m_textAlignRule;
static css::Rule m_textColorRule;
static css::Rule m_paddingLeftRule;

View File

@ -9,8 +9,6 @@
#include "config.h"
#endif
//#define CLOSE_BUTTON_IN_EACH_TAB
#include "app/modules/gfx.h"
#include "app/modules/gui.h"
#include "app/ui/skin/skin_theme.h"
@ -45,26 +43,6 @@ static WidgetType tabs_type()
return type;
}
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:
bool onProcessMessage(Message* msg) override;
void onDisable() override;
private:
int m_direction;
Tabs* m_tabs;
};
Tabs::Tabs(TabsDelegate* delegate)
: Widget(tabs_type())
, m_delegate(delegate)
@ -73,32 +51,12 @@ Tabs::Tabs(TabsDelegate* delegate)
setDoubleBuffered(true);
m_hot = NULL;
m_hotCloseButton = false;
m_selected = NULL;
m_scrollX = 0;
m_ani = ANI_NONE;
m_removedTab = NULL;
m_button_left = new ScrollButton(-1, this);
m_button_right = new ScrollButton(+1, this);
setup_mini_look(m_button_left);
setup_mini_look(m_button_right);
setup_bevels(m_button_left, 2, 0, 2, 0);
setup_bevels(m_button_right, 0, 2, 0, 2);
m_button_left->setFocusStop(false);
m_button_right->setFocusStop(false);
set_gfxicon_to_button(m_button_left,
PART_COMBOBOX_ARROW_LEFT,
PART_COMBOBOX_ARROW_LEFT_SELECTED,
PART_COMBOBOX_ARROW_LEFT_DISABLED, JI_CENTER | JI_MIDDLE);
set_gfxicon_to_button(m_button_right,
PART_COMBOBOX_ARROW_RIGHT,
PART_COMBOBOX_ARROW_RIGHT_SELECTED,
PART_COMBOBOX_ARROW_RIGHT_DISABLED, JI_CENTER | JI_MIDDLE);
initTheme();
}
@ -116,15 +74,12 @@ Tabs::~Tabs()
for (Tab* tab : m_list)
delete tab;
m_list.clear();
delete m_button_left; // widget
delete m_button_right; // widget
}
void Tabs::addTab(TabView* tabView)
{
Tab* tab = new Tab(tabView);
calcTabWidth(tab);
tab->text = tab->view->getTabText();
m_list.push_back(tab);
@ -156,7 +111,6 @@ void Tabs::removeTab(TabView* tabView)
it = m_list.erase(it);
// Width of the removed tab
if (m_removedTab) {
delete m_removedTab;
m_removedTab = NULL;
@ -177,10 +131,8 @@ void Tabs::removeTab(TabView* tabView)
void Tabs::updateTabsText()
{
for (Tab* tab : m_list) {
// Change text of the tab
calcTabWidth(tab);
}
for (Tab* tab : m_list)
tab->text = tab->view->getTabText();
invalidate();
}
@ -258,7 +210,6 @@ bool Tabs::onProcessMessage(Message* msg)
return true;
case kMouseDownMessage:
case kMouseUpMessage:
if (m_hot != NULL) {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
@ -267,13 +218,41 @@ bool Tabs::onProcessMessage(Message* msg)
invalidate();
}
if (m_hotCloseButton) {
if (!m_clickedCloseButton) {
m_clickedCloseButton = true;
invalidate();
}
}
// 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_clickedCloseButton &&
mouseMsg->left()) {
m_delegate->clickTab(this, m_selected->view, mouseMsg->buttons());
}
captureMouse();
}
return true;
case kMouseUpMessage:
if (hasCapture()) {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
if (m_delegate && m_selected && m_selected == m_hot) {
if (m_hotCloseButton && m_clickedCloseButton) {
m_clickedCloseButton = false;
invalidate();
m_delegate->clickClose(this, m_selected->view);
}
else if (!mouseMsg->left()) {
m_delegate->clickTab(this, m_selected->view, mouseMsg->buttons());
}
}
releaseMouse();
}
return true;
@ -303,12 +282,6 @@ bool Tabs::onProcessMessage(Message* msg)
case ANI_NONE:
// Do nothing
break;
case ANI_SCROLL: {
ScrollButton* button = dynamic_cast<ScrollButton*>(getManager()->getCapture());
if (button != NULL)
setScrollX(m_scrollX + button->getDirection()*8*static_cast<TimerMessage*>(msg)->count());
break;
}
case ANI_SMOOTH_SCROLL: {
if (m_ani_t == ANI_SMOOTH_SCROLL_TICKS) {
stopAni();
@ -366,8 +339,9 @@ void Tabs::onPaint(PaintEvent& ev)
box.x = box.x2();
// For each tab...
int tabWidth = calcTabWidth();
for (Tab* tab : m_list) {
box.w = tab->width;
box.w = tabWidth;
int x_delta = 0;
int y_delta = 0;
@ -377,19 +351,19 @@ void Tabs::onPaint(PaintEvent& ev)
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) {
x_delta += m_removedTab->width
- int(double(m_removedTab->width)*(1.0-std::exp(-10.0 * m_ani_t / (double)ANI_REMOVING_TAB_TICKS)));
x_delta = MID(0, x_delta, m_removedTab->width);
x_delta += tabWidth
- int(double(tabWidth)*(1.0-std::exp(-10.0 * m_ani_t / (double)ANI_REMOVING_TAB_TICKS)));
x_delta = MID(0, x_delta, tabWidth);
// Draw deleted tab
if (m_removedTab) {
gfx::Rect box2(box.x, box.y, x_delta, box.h);
drawTab(g, box2, m_removedTab, 0, false);
drawTab(g, box2, m_removedTab, 0, false, false);
}
}
box.x += x_delta;
drawTab(g, box, tab, y_delta, (tab == m_selected));
drawTab(g, box, tab, y_delta, (tab == m_hot), (tab == m_selected));
box.x = box.x2();
}
@ -397,12 +371,12 @@ void Tabs::onPaint(PaintEvent& ev)
if (m_ani == ANI_REMOVING_TAB && m_nextTabOfTheRemovedOne == NULL) {
// Draw deleted tab
if (m_removedTab) {
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)));
x_delta = MID(0, x_delta, m_removedTab->width);
int x_delta = tabWidth
- int(double(tabWidth)*(1.0-std::exp(-10.0 * m_ani_t / (double)ANI_REMOVING_TAB_TICKS)));
x_delta = MID(0, x_delta, tabWidth);
gfx::Rect box2(box.x, box.y, x_delta, box.h);
drawTab(g, box2, m_removedTab, 0, false);
drawTab(g, box2, m_removedTab, 0, false, false);
box.x += x_delta;
box.w = 0;
@ -437,22 +411,6 @@ void Tabs::onPreferredSize(PreferredSizeEvent& ev)
ev.setPreferredSize(reqsize);
}
void Tabs::onInitTheme(InitThemeEvent& ev)
{
Widget::onInitTheme(ev);
SkinTheme* theme = static_cast<SkinTheme*>(ev.getTheme());
m_button_left->setBgColor(theme->colors.tabActiveFace());
m_button_right->setBgColor(theme->colors.tabActiveFace());
}
void Tabs::onSetText()
{
for (Tab* tab : m_list)
calcTabWidth(tab);
}
void Tabs::selectTabInternal(Tab* tab)
{
m_selected = tab;
@ -460,15 +418,30 @@ void Tabs::selectTabInternal(Tab* tab)
invalidate();
}
void Tabs::drawTab(Graphics* g, const gfx::Rect& box, Tab* tab, int y_delta, bool selected)
void Tabs::drawTab(Graphics* g, const gfx::Rect& _box, Tab* tab, int y_delta,
bool hover, bool selected)
{
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)
box.w = ui::guiscale()*8;
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
gfx::Color text_color;
gfx::Color face_color;
int clipTextRightSide;
gfx::Rect closeBox = getTabCloseButtonBounds(box);
if (closeBox.isEmpty())
clipTextRightSide = 4*ui::guiscale();
else {
closeBox.y += y_delta;
clipTextRightSide = closeBox.w;
}
// Selected
if (selected) {
@ -483,8 +456,14 @@ void Tabs::drawTab(Graphics* g, const gfx::Rect& box, Tab* tab, int y_delta, boo
skin::Style::State state;
if (selected) state += skin::Style::active();
if (hover) state += skin::Style::hover();
if (box.w > 2) {
theme->styles.tab()->paint(g,
gfx::Rect(box.x, box.y+y_delta, box.w, box.h),
nullptr, state);
if (box.w > 8*ui::guiscale()) {
IntersectClip clip(g, gfx::Rect(box.x, box.y+y_delta, box.w-clipTextRightSide, box.h));
theme->styles.tab()->paint(g,
gfx::Rect(box.x, box.y+y_delta, box.w, box.h),
tab->text.c_str(), state);
@ -494,12 +473,28 @@ void Tabs::drawTab(Graphics* g, const gfx::Rect& box, Tab* tab, int y_delta, boo
gfx::Rect(box.x, box.y2(), box.w, getBounds().y2()-box.y2()),
nullptr, state);
#ifdef CLOSE_BUTTON_IN_EACH_TAB
she::Surface* close_icon = theme->get_part(PART_WINDOW_CLOSE_BUTTON_NORMAL);
g->drawRgbaSurface(close_icon,
box.x2() - 4*guiscale() - close_icon->width(),
box.y + box.h/2 - close_icon->height()/2+1 * guiscale());
#endif
// Close button
if (!closeBox.isEmpty()) {
skin::Style* style = theme->styles.tabCloseIcon();
if (m_delegate &&
m_delegate->isModified(this, tab->view) &&
(!hover || !m_hotCloseButton)) {
style = theme->styles.tabModifiedIcon();
}
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);
}
}
Tabs::TabsListIterator Tabs::getTabIteratorByView(TabView* tabView)
@ -527,10 +522,11 @@ int Tabs::getMaxScrollX()
{
TabsListIterator it, end = m_list.end();
int x = 0;
int tabWidth = calcTabWidth();
for (it = m_list.begin(); it != end; ++it) {
Tab* tab = *it;
x += tab->width;
x += tabWidth;
}
x -= getBounds().w;
@ -545,19 +541,20 @@ void Tabs::makeTabVisible(Tab* make_visible_this_tab)
{
int x = 0;
int extra_x = getMaxScrollX() > 0 ? ARROW_W*2: 0;
int tabWidth = calcTabWidth();
for (Tab* tab : m_list) {
if (tab == make_visible_this_tab) {
if (x - m_scrollX < 0) {
setScrollX(x);
}
else if (x + tab->width - m_scrollX > getBounds().w - extra_x) {
setScrollX(x + tab->width - getBounds().w + extra_x);
else if (x + tabWidth - m_scrollX > getBounds().w - extra_x) {
setScrollX(x + tabWidth - getBounds().w + extra_x);
}
break;
}
x += tab->width;
x += tabWidth;
}
}
@ -571,56 +568,35 @@ void Tabs::setScrollX(int scroll_x)
calculateHot();
invalidate();
}
// We need scroll buttons?
if (max_x > 0) {
// Add childs
if (!HAS_ARROWS(this)) {
addChild(m_button_left);
addChild(m_button_right);
invalidate();
}
/* disable/enable buttons */
m_button_left->setEnabled(m_scrollX > 0);
m_button_right->setEnabled(m_scrollX < max_x);
// 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);
box.x += ARROW_W;
m_button_right->setBounds(box);
}
// Remove buttons
else if (HAS_ARROWS(this)) {
removeChild(m_button_left);
removeChild(m_button_right);
invalidate();
}
}
void Tabs::calculateHot()
{
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
gfx::Rect rect = getBounds();
gfx::Rect box(rect.x-m_scrollX, rect.y, 0, rect.h-1);
Tab *hot = NULL;
gfx::Point mousePos = ui::get_mouse_position();
Tab* hot = NULL;
bool hotCloseButton = false;
int tabWidth = calcTabWidth();
// For each tab
for (Tab* tab : m_list) {
box.w = tab->width;
box.w = tabWidth;
if (box.contains(ui::get_mouse_position())) {
if (box.contains(mousePos)) {
hot = tab;
hotCloseButton = getTabCloseButtonBounds(box).contains(mousePos);
break;
}
box.x += box.w;
}
if (m_hot != hot) {
if (m_hot != hot ||
m_hotCloseButton != hotCloseButton) {
m_hot = hot;
m_hotCloseButton = hotCloseButton;
if (m_delegate)
m_delegate->mouseOverTab(this, m_hot ? m_hot->view: NULL);
@ -629,29 +605,29 @@ void Tabs::calculateHot()
}
}
void Tabs::calcTabWidth(Tab* tab)
int Tabs::calcTabWidth()
{
// Cache current tab text
tab->text = tab->view->getTabText();
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
int tabWidth = theme->dimensions.tabsWidth();
int border = 4*guiscale();
#ifdef CLOSE_BUTTON_IN_EACH_TAB
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
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);
#else
tab->width = (border + getFont()->textLength(tab->text.c_str()) + border);
#endif
if (tabWidth * m_list.size() > (size_t)getBounds().w) {
tabWidth = getBounds().w / m_list.size();
tabWidth = MAX(2*ui::guiscale(), tabWidth);
}
return tabWidth;
}
void Tabs::startScrolling()
gfx::Rect Tabs::getTabCloseButtonBounds(const gfx::Rect& box)
{
startAni(ANI_SCROLL);
}
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
int iconW = theme->dimensions.tabsCloseIconWidth();
int iconH = theme->dimensions.tabsCloseIconHeight();
void Tabs::stopScrolling()
{
stopAni();
if (box.w-iconW > 4*ui::guiscale())
return gfx::Rect(box.x2()-iconW, box.y+box.h/2-iconH/2, iconW, iconH);
else
return gfx::Rect();
}
void Tabs::startAni(Ani ani)
@ -671,38 +647,4 @@ void Tabs::stopAni()
m_timer.stop();
}
bool Tabs::ScrollButton::onProcessMessage(Message* msg)
{
switch (msg->type()) {
case kMouseDownMessage:
captureMouse();
m_tabs->startScrolling();
return true;
case kMouseUpMessage:
if (hasCapture())
releaseMouse();
m_tabs->stopScrolling();
return true;
}
return Button::onProcessMessage(msg);
}
void Tabs::ScrollButton::onDisable()
{
Button::onDisable();
if (hasCapture())
releaseMouse();
if (isSelected()) {
m_tabs->stopScrolling();
setSelected(false);
}
}
} // namespace app

View File

@ -40,9 +40,14 @@ namespace app {
// Called when the user presses a mouse button over a tab.
virtual void clickTab(Tabs* tabs, TabView* tabView, ui::MouseButtons buttons) = 0;
// When the tab close button is pressed.
virtual void clickClose(Tabs* tabs, TabView* tabView) = 0;
// Called when the mouse is over a tab (the data can be null if the
// mouse just leave all tabs)
virtual void mouseOverTab(Tabs* tabs, TabView* tabView) = 0;
virtual bool isModified(Tabs* tabs, TabView* tabView) = 0;
};
// Tabs control. Used to show opened documents.
@ -50,9 +55,8 @@ namespace app {
struct Tab {
TabView* view;
std::string text;
int width;
Tab(TabView* view) : view(view), width(0) {
Tab(TabView* view) : view(view) {
}
};
@ -62,7 +66,6 @@ namespace app {
enum Ani { ANI_NONE,
ANI_ADDING_TAB,
ANI_REMOVING_TAB,
ANI_SCROLL,
ANI_SMOOTH_SCROLL };
public:
@ -78,33 +81,31 @@ namespace app {
void selectPreviousTab();
TabView* getSelectedTab();
void startScrolling();
void stopScrolling();
protected:
bool onProcessMessage(ui::Message* msg) override;
void onPaint(ui::PaintEvent& ev) override;
void onResize(ui::ResizeEvent& ev) override;
void onPreferredSize(ui::PreferredSizeEvent& ev) override;
void onInitTheme(ui::InitThemeEvent& ev) override;
void onSetText() override;
private:
void startAni(Ani ani);
void stopAni();
void selectTabInternal(Tab* tab);
void drawTab(ui::Graphics* g, const gfx::Rect& box, Tab* tab, int y_delta, bool selected);
void drawTab(ui::Graphics* g, const gfx::Rect& box, Tab* tab, int y_delta, bool hover, bool selected);
TabsListIterator getTabIteratorByView(TabView* tabView);
Tab* getTabByView(TabView* tabView);
int getMaxScrollX();
void makeTabVisible(Tab* tab);
void setScrollX(int scroll_x);
void calculateHot();
void calcTabWidth(Tab* tab);
int calcTabWidth();
gfx::Rect getTabCloseButtonBounds(const gfx::Rect& box);
TabsList m_list;
Tab* m_hot;
bool m_hotCloseButton;
bool m_clickedCloseButton;
Tab* m_selected;
int m_scrollX;
@ -119,11 +120,6 @@ namespace app {
int m_ani_t; // Number of ticks from the beginning of the animation
Tab* m_removedTab;
Tab* m_nextTabOfTheRemovedOne;
// Buttons to scroll tabs (useful when there are more tabs than visible area)
class ScrollButton;
ScrollButton* m_button_left;
ScrollButton* m_button_right;
};
} // namespace app