The color bar is now resizable.

- Added Splitter::Type so it can be by pixel or by percentage.
- Added Widget::load/saveLayout functions and Widget::onLoad/SaveLayout
  events.
- Added LayoutIO interface so we can delegate the serialization of the
  layout.
This commit is contained in:
David Capello 2012-07-18 01:10:43 -03:00
parent 25fc113c37
commit 525851e6a1
22 changed files with 311 additions and 30 deletions

View File

@ -5,8 +5,12 @@
<box noborders="true" horizontal="true" id="menubar" />
<box noborders="true" horizontal="true" id="tabsbar" />
<box noborders="true" horizontal="true" expansive="true">
<box noborders="true" vertical="true" id="colorbar" />
<box noborders="true" horizontal="true" id="editor" expansive="true" />
<splitter id="colorbarsplitter"
horizontal="true" expansive="true"
by="pixel" position="52">
<box noborders="true" vertical="true" id="colorbar" />
<box noborders="true" horizontal="true" id="editor" expansive="true" />
</splitter>
<box noborders="true" vertical="true" id="toolbar" />
</box>
<box noborders="true" horizontal="true" id="statusbar" />

View File

@ -293,9 +293,20 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
else if (ustrcmp(elem_name, "splitter") == 0) {
bool horizontal = bool_attr_is_true(elem, "horizontal");
bool vertical = bool_attr_is_true(elem, "vertical");
const char* by = elem->Attribute("by");
const char* position = elem->Attribute("position");
Splitter::Type type = (by && strcmp(by, "pixel") == 0 ?
Splitter::ByPixel:
Splitter::ByPercentage);
widget = new Splitter(horizontal ? JI_HORIZONTAL:
vertical ? JI_VERTICAL: 0);
Splitter* splitter = new Splitter(type,
horizontal ? JI_HORIZONTAL:
vertical ? JI_VERTICAL: 0);
if (position) {
splitter->setPosition(strtod(position, NULL)
* (type == Splitter::ByPixel ? jguiscale(): 1));
}
widget = splitter;
}
/* radio */
else if (ustrcmp(elem_name, "radio") == 0) {

View File

@ -449,7 +449,7 @@ void split_editor(Editor* editor, int align)
Widget* parent_box = view->getParent(); // Box or splitter.
// Create a new box to contain both editors, and a new view to put the new editor.
Widget* new_splitter = new Splitter(align);
Widget* new_splitter = new Splitter(Splitter::ByPercentage, align);
View* new_view = new EditorView(EditorView::CurrentEditorMode);
Editor* new_editor = create_new_editor();

View File

@ -125,9 +125,15 @@ static Shortcut* get_keyboard_shortcut_for_spriteeditor(const char* action_name)
//////////////////////////////////////////////////////////////////////
class CustomizedGuiManager : public Manager
, public LayoutIO
{
protected:
bool onProcessMessage(Message* msg) OVERRIDE;
LayoutIO* onGetLayoutIO() OVERRIDE { return this; }
// LayoutIO implementation
std::string loadLayout(Widget* widget) OVERRIDE;
void saveLayout(Widget* widget, const std::string& str) OVERRIDE;
};
static CustomizedGuiManager* manager = NULL;
@ -1057,6 +1063,28 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
return Manager::onProcessMessage(msg);
}
std::string CustomizedGuiManager::loadLayout(Widget* widget)
{
if (widget->getRoot() == NULL)
return "";
std::string rootId = widget->getRoot()->getId();
std::string widgetId = widget->getId();
return get_config_string(("layout:"+rootId).c_str(), widgetId.c_str(), "");
}
void CustomizedGuiManager::saveLayout(Widget* widget, const std::string& str)
{
if (widget->getRoot() == NULL)
return;
std::string rootId = widget->getRoot()->getId();
std::string widgetId = widget->getId();
set_config_string(("layout:"+rootId).c_str(), widgetId.c_str(), str.c_str());
}
// Slot for App::PaletteChange to regenerate graphics when the App palette is changed
static void on_palette_change_signal()
{

View File

@ -1196,9 +1196,10 @@ void SkinTheme::draw_menuitem(MenuItem* widget, JRect clip)
}
}
void SkinTheme::draw_panel(Widget* widget, JRect clip)
void SkinTheme::drawSplitter(PaintEvent& ev)
{
jdraw_rectfill(widget->rc, get_panel_face_color());
Splitter* splitter = static_cast<Splitter*>(ev.getSource());
jdraw_rectfill(splitter->rc, get_splitter_normal_face_color());
}
void SkinTheme::paintRadioButton(PaintEvent& ev)

View File

@ -80,7 +80,7 @@ public:
void draw_listitem(ui::Widget* widget, ui::JRect clip);
void draw_menu(ui::Menu* menu, ui::JRect clip);
void draw_menuitem(ui::MenuItem* menuitem, ui::JRect clip);
void draw_panel(ui::Widget* widget, ui::JRect clip);
void drawSplitter(ui::PaintEvent& ev);
void paintRadioButton(ui::PaintEvent& ev);
void draw_separator(ui::Widget* widget, ui::JRect clip);
void paintSlider(ui::PaintEvent& ev);
@ -138,7 +138,7 @@ public:
int get_tab_normal_text_color() const { return makecol(0, 0, 0); }
int get_tab_normal_face_color() const { return makecol(199, 199, 199); }
int get_panel_face_color() const { return makecol(125, 146, 158); }
int get_splitter_normal_face_color() const { return makecol(125, 146, 158); }
int get_scrollbar_bg_face_color() const { return makecol(125, 146, 158); }
int get_scrollbar_thumb_face_color() const { return makecol(199, 199, 199); }

View File

@ -26,8 +26,10 @@
#include "ui/image_view.h"
#include "ui/init_theme_event.h"
#include "ui/label.h"
#include "ui/layout_io.h"
#include "ui/link_label.h"
#include "ui/listbox.h"
#include "ui/load_layout_event.h"
#include "ui/manager.h"
#include "ui/menu.h"
#include "ui/message.h"
@ -38,6 +40,7 @@
#include "ui/property.h"
#include "ui/rect.h"
#include "ui/region.h"
#include "ui/save_layout_event.h"
#include "ui/scroll_bar.h"
#include "ui/separator.h"
#include "ui/slider.h"

26
src/ui/layout_io.h Normal file
View File

@ -0,0 +1,26 @@
// ASEPRITE gui library
// Copyright (C) 2001-2012 David Capello
//
// This source file is ditributed under a BSD-like license, please
// read LICENSE.txt for more information.
#ifndef UI_LAYOUT_IO_H_INCLUDED
#define UI_LAYOUT_IO_H_INCLUDED
#include <string>
namespace ui {
class Widget;
class LayoutIO
{
public:
virtual ~LayoutIO() { }
virtual std::string loadLayout(Widget* widget) = 0;
virtual void saveLayout(Widget* widget, const std::string& str) = 0;
};
} // namespace ui
#endif

View File

@ -0,0 +1,33 @@
// ASEPRITE gui library
// Copyright (C) 2001-2012 David Capello
//
// This source file is ditributed under a BSD-like license, please
// read LICENSE.txt for more information.
#ifndef UI_LOAD_LAYOUT_EVENT_H_INCLUDED
#define UI_LOAD_LAYOUT_EVENT_H_INCLUDED
#include "ui/event.h"
#include <iosfwd>
namespace ui {
class Widget;
class LoadLayoutEvent : public Event
{
public:
LoadLayoutEvent(Widget* source, std::istream& stream)
: Event(source)
, m_stream(stream) {
}
std::istream& stream() { return m_stream; }
private:
std::istream& m_stream;
};
} // namespace ui
#endif

View File

@ -940,6 +940,11 @@ void Manager::onBroadcastMouseMessage(WidgetsList& targets)
widget->broadcastMouseMessage(targets);
}
LayoutIO* Manager::onGetLayoutIO()
{
return NULL;
}
void Manager::onPreferredSize(PreferredSizeEvent& ev)
{
int w = 0, h = 0;
@ -1156,6 +1161,11 @@ void Manager::invalidateDisplayRegion(const JRegion region)
jregion_free(reg2);
}
LayoutIO* Manager::getLayoutIO()
{
return onGetLayoutIO();
}
void Manager::collectGarbage()
{
if (m_garbage.empty())

View File

@ -12,8 +12,9 @@
namespace ui {
class Window;
class LayoutIO;
class Timer;
class Window;
class Manager : public Widget
{
@ -60,6 +61,8 @@ namespace ui {
void invalidateDisplayRegion(const JRegion region);
LayoutIO* getLayoutIO();
void _openWindow(Window* window);
void _closeWindow(Window* window, bool redraw_background);
@ -67,6 +70,7 @@ namespace ui {
bool onProcessMessage(Message* msg) OVERRIDE;
void onPreferredSize(PreferredSizeEvent& ev) OVERRIDE;
void onBroadcastMouseMessage(WidgetsList& targets) OVERRIDE;
virtual LayoutIO* onGetLayoutIO();
private:
void layoutManager(JRect rect);

View File

@ -0,0 +1,33 @@
// ASEPRITE gui library
// Copyright (C) 2001-2012 David Capello
//
// This source file is ditributed under a BSD-like license, please
// read LICENSE.txt for more information.
#ifndef UI_SAVE_LAYOUT_EVENT_H_INCLUDED
#define UI_SAVE_LAYOUT_EVENT_H_INCLUDED
#include "ui/event.h"
#include <iosfwd>
namespace ui {
class Widget;
class SaveLayoutEvent : public Event
{
public:
SaveLayoutEvent(Widget* source, std::ostream& stream)
: Event(source)
, m_stream(stream) {
}
std::ostream& stream() { return m_stream; }
private:
std::ostream& m_stream;
};
} // namespace ui
#endif

View File

@ -8,8 +8,10 @@
#include "ui/splitter.h"
#include "ui/load_layout_event.h"
#include "ui/message.h"
#include "ui/preferred_size_event.h"
#include "ui/save_layout_event.h"
#include "ui/system.h"
#include "ui/theme.h"
@ -17,8 +19,9 @@ using namespace gfx;
namespace ui {
Splitter::Splitter(int align)
Splitter::Splitter(Type type, int align)
: Widget(JI_SPLITTER)
, m_type(type)
, m_pos(50)
{
setAlign(align);
@ -44,10 +47,6 @@ bool Splitter::onProcessMessage(Message* msg)
layoutMembers(&msg->setpos.rect);
return true;
case JM_DRAW:
getTheme()->draw_panel(this, &msg->draw.rect);
return true;
case JM_BUTTONPRESSED:
if (isEnabled()) {
Widget* c1, *c2;
@ -94,14 +93,28 @@ bool Splitter::onProcessMessage(Message* msg)
case JM_MOTION:
if (this->hasCapture()) {
if (this->getAlign() & JI_HORIZONTAL) {
m_pos = 100.0 * (msg->mouse.x-this->rc->x1) / jrect_w(this->rc);
switch (m_type) {
case ByPercentage:
m_pos = 100.0 * (msg->mouse.x-this->rc->x1) / jrect_w(this->rc);
m_pos = MID(0, m_pos, 100);
break;
case ByPixel:
m_pos = msg->mouse.x-this->rc->x1;
break;
}
}
else {
m_pos = 100.0 * (msg->mouse.y-this->rc->y1) / jrect_h(this->rc);
switch (m_type) {
case ByPercentage:
m_pos = 100.0 * (msg->mouse.y-this->rc->y1) / jrect_h(this->rc);
m_pos = MID(0, m_pos, 100);
break;
case ByPixel:
m_pos = (msg->mouse.y-this->rc->y1);
break;
}
}
m_pos = MID(0, m_pos, 100);
jwidget_set_rect(this, this->rc);
invalidate();
return true;
@ -154,8 +167,6 @@ bool Splitter::onProcessMessage(Message* msg)
jmouse_set_cursor(JI_CURSOR_SIZE_T);
return true;
}
else
return false;
}
break;
@ -164,6 +175,11 @@ bool Splitter::onProcessMessage(Message* msg)
return Widget::onProcessMessage(msg);
}
void Splitter::onPaint(PaintEvent& ev)
{
getTheme()->drawSplitter(ev);
}
void Splitter::onPreferredSize(PreferredSizeEvent& ev)
{
#define GET_CHILD_SIZE(w, h) \
@ -218,6 +234,24 @@ void Splitter::onPreferredSize(PreferredSizeEvent& ev)
ev.setPreferredSize(Size(w, h));
}
void Splitter::onLoadLayout(LoadLayoutEvent& ev)
{
ev.stream() >> m_pos;
// Do for all children
UI_FOREACH_WIDGET(getChildren(), it)
(*it)->loadLayout();
}
void Splitter::onSaveLayout(SaveLayoutEvent& ev)
{
ev.stream() << m_pos;
// Do for all children
UI_FOREACH_WIDGET(getChildren(), it)
(*it)->saveLayout();
}
void Splitter::layoutMembers(JRect rect)
{
#define FIXUP(x, y, w, h, l, t, r, b) \
@ -226,8 +260,16 @@ void Splitter::layoutMembers(JRect rect)
\
pos->x##1 = this->rc->x##1; \
pos->y##1 = this->rc->y##1; \
pos->x##2 = pos->x##1 + avail*m_pos/100; \
/* TODO uncomment this to make a restricted panel */ \
switch (m_type) { \
case ByPercentage: \
pos->x##2 = pos->x##1 + avail*m_pos/100; \
break; \
case ByPixel: \
pos->x##2 = pos->x##1 + m_pos; \
break; \
} \
\
/* TODO uncomment this to make a restricted splitter */ \
/* pos->w = MID(reqSize1.w, pos->w, avail-reqSize2.w); */ \
pos->y##2 = pos->y##1 + jrect_##h(this->rc); \
\

View File

@ -15,7 +15,9 @@ namespace ui {
class Splitter : public Widget
{
public:
Splitter(int align);
enum Type { ByPercentage, ByPixel };
Splitter(Type type, int align);
double getPosition() const;
void setPosition(double pos);
@ -23,11 +25,15 @@ namespace ui {
protected:
// Events
bool onProcessMessage(Message* msg) OVERRIDE;
void onPaint(PaintEvent& ev) OVERRIDE;
void onPreferredSize(PreferredSizeEvent& ev) OVERRIDE;
void onLoadLayout(LoadLayoutEvent& ev) OVERRIDE;
void onSaveLayout(SaveLayoutEvent& ev) OVERRIDE;
private:
void layoutMembers(JRect rect);
Type m_type;
double m_pos;
};

View File

@ -60,7 +60,7 @@ namespace ui {
virtual void draw_listitem(Widget* widget, JRect clip) = 0;
virtual void draw_menu(Menu* menu, JRect clip) = 0;
virtual void draw_menuitem(MenuItem* menuitem, JRect clip) = 0;
virtual void draw_panel(Widget* widget, JRect clip) = 0;
virtual void drawSplitter(PaintEvent& ev) = 0;
virtual void paintRadioButton(PaintEvent& ev) = 0;
virtual void draw_separator(Widget* widget, JRect clip) = 0;
virtual void paintSlider(PaintEvent& ev) = 0;

View File

@ -8,18 +8,19 @@
#include "config.h"
#include "base/memory.h"
#include "ui/gui.h"
#include "ui/intern.h"
#include <cctype>
#include <climits>
#include <cstdarg>
#include <cstring>
#include <queue>
#include <cstdio>
#include <sstream>
#include <allegro.h>
#include "base/memory.h"
#include "ui/gui.h"
#include "ui/intern.h"
using namespace gfx;
namespace ui {
@ -523,6 +524,45 @@ void Widget::layout()
invalidate();
}
void Widget::loadLayout()
{
if (!m_id.empty()) {
LayoutIO* io = getManager()->getLayoutIO();
if (io) {
std::string layout = io->loadLayout(this);
if (!layout.empty()) {
std::stringstream s(layout);
LoadLayoutEvent ev(this, s);
onLoadLayout(ev);
}
}
}
// Do for all children
UI_FOREACH_WIDGET(m_children, it)
(*it)->loadLayout();
}
void Widget::saveLayout()
{
if (!m_id.empty()) {
LayoutIO* io = getManager()->getLayoutIO();
if (io) {
std::stringstream s;
SaveLayoutEvent ev(this, s);
onSaveLayout(ev);
std::string layout = s.str();
if (!layout.empty())
io->saveLayout(this, layout);
}
}
// Do for all children
UI_FOREACH_WIDGET(m_children, it)
(*it)->saveLayout();
}
/**********************************************************************/
/* position and geometry */
@ -1383,6 +1423,16 @@ void Widget::onPreferredSize(PreferredSizeEvent& ev)
ev.setPreferredSize(sz);
}
void Widget::onLoadLayout(LoadLayoutEvent& ev)
{
// Do nothing
}
void Widget::onSaveLayout(SaveLayoutEvent& ev)
{
// Do nothing
}
void Widget::onPaint(PaintEvent& ev)
{
// Do nothing

View File

@ -27,9 +27,11 @@ namespace ui {
union Message;
class InitThemeEvent;
class LoadLayoutEvent;
class Manager;
class PaintEvent;
class PreferredSizeEvent;
class SaveLayoutEvent;
class Theme;
class Window;
@ -243,6 +245,8 @@ namespace ui {
// ===============================================================
void layout();
void loadLayout();
void saveLayout();
// ===============================================================
// POSITION & GEOMETRY
@ -322,6 +326,8 @@ namespace ui {
virtual void onInvalidateRegion(const JRegion region);
virtual void onPreferredSize(PreferredSizeEvent& ev);
virtual void onLoadLayout(LoadLayoutEvent& ev);
virtual void onSaveLayout(SaveLayoutEvent& ev);
virtual void onPaint(PaintEvent& ev);
virtual void onBroadcastMouseMessage(WidgetsList& targets);
virtual void onInitTheme(InitThemeEvent& ev);

View File

@ -192,6 +192,9 @@ void Window::remap_window()
jwidget_set_rect(this, rect);
jrect_free(rect);
// load layout
loadLayout();
invalidate();
}
@ -281,6 +284,10 @@ bool Window::onProcessMessage(Message* msg)
m_killer = NULL;
break;
case JM_CLOSE:
saveLayout();
break;
case JM_BUTTONPRESSED: {
if (!m_is_moveable)
break;

View File

@ -54,7 +54,6 @@ ColorSelector::ColorSelector()
m_topBox.setBorder(gfx::Border(0));
m_topBox.child_spacing = 0;
m_colorPalette.setColumns(40);
m_colorPalette.setBoxSize(6*jguiscale());
m_colorPaletteContainer.attachToView(&m_colorPalette);

View File

@ -48,6 +48,8 @@ public:
MainWindow::MainWindow()
: Window(true, NULL)
{
setId("main_window");
// Load all menus by first time.
AppMenus::instance()->reload();

View File

@ -55,6 +55,7 @@ PaletteView::PaletteView(bool editable)
, m_currentEntry(-1)
, m_rangeAnchor(-1)
, m_selectedEntries(Palette::MaxColors, false)
, m_isUpdatingColumns(false)
{
m_editable = editable;
m_columns = 16;
@ -204,6 +205,20 @@ bool PaletteView::onProcessMessage(Message* msg)
request_size(&msg->reqsize.w, &msg->reqsize.h);
return true;
case JM_SETPOS:
if (!m_isUpdatingColumns) {
m_isUpdatingColumns = true;
View* view = View::getView(this);
if (view) {
int columns =
(view->getViewportBounds().w-this->child_spacing*2)
/ (m_boxsize+this->child_spacing);
setColumns(MID(1, columns, Palette::MaxColors));
}
m_isUpdatingColumns = false;
}
break;
case JM_DRAW: {
div_t d = div(Palette::MaxColors, m_columns);
int cols = m_columns;

View File

@ -63,6 +63,7 @@ private:
int m_currentEntry;
int m_rangeAnchor;
SelectedEntries m_selectedEntries;
bool m_isUpdatingColumns;
};
int palette_view_type();