mirror of
https://github.com/aseprite/aseprite.git
synced 2024-12-27 21:19:18 +00:00
Add close/modified icon to tabs
This commit is contained in:
parent
13611eb23e
commit
f4e01345c6
Binary file not shown.
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@ -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>
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user