Add "Size" options in CanvasSizeCommand to specify width/height

- Added canvas icons to change the expansion direction in CanvasSizeCommand
- Added Widget::at() and Widget::offerCapture() member functions
- Improved ButtonSet widget
This commit is contained in:
David Capello 2014-09-08 02:27:41 -03:00
parent 81bfd5da55
commit d14a0fe075
16 changed files with 604 additions and 285 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -339,6 +339,16 @@
<part id="freehand_algo_pixel_perfect_selected" x="168" y="208" w="8" h="8" />
<part id="freehand_algo_dots" x="176" y="208" w="8" h="8" />
<part id="freehand_algo_dots_selected" x="184" y="208" w="8" h="8" />
<part id="canvas_nw" x="96" y="96" w="16" h="16" />
<part id="canvas_n" x="112" y="96" w="16" h="16" />
<part id="canvas_ne" x="128" y="96" w="16" h="16" />
<part id="canvas_w" x="96" y="112" w="16" h="16" />
<part id="canvas_c" x="112" y="112" w="16" h="16" />
<part id="canvas_e" x="128" y="112" w="16" h="16" />
<part id="canvas_sw" x="96" y="128" w="16" h="16" />
<part id="canvas_s" x="112" y="128" w="16" h="16" />
<part id="canvas_se" x="128" y="128" w="16" h="16" />
<part id="canvas_empty" x="96" y="96" w="1" h="1" />
</parts>
<stylesheet>

View File

@ -1,32 +1,53 @@
<!-- ASEPRITE -->
<!-- Copyright (C) 2001-2013 by David Capello -->
<!-- Copyright (C) 2001-2014 by David Capello -->
<gui>
<box vertical="true" id="main_box">
<box horizontal="true">
<box vertical="true" homogeneous="true">
<label text="Left:" />
<label text="Right:" />
</box>
<box vertical="true" homogeneous="true">
<entry text="0" id="left" maxsize="32" maxwidth="64" tooltip="Columns to be added/removed in the left side.&#10;Use a negative number to remove columns." />
<entry text="0" id="right" maxsize="32" maxwidth="64" tooltip="Columns to be added/removed in the right side.&#10;Use a negative number to remove columns." />
</box>
<box vertical="true" homogeneous="true">
<label text="Top:" />
<label text="Bottom:" />
</box>
<box vertical="true" homogeneous="true">
<entry text="0" id="top" maxsize="32" maxwidth="64" tooltip="Rows to be added/removed in the top side.&#10;Use a negative number to remove rows." />
<entry text="0" id="bottom" maxsize="32" maxwidth="64" tooltip="Rows to be added/removed in the bottom side.&#10;Use a negative number to remove rows." />
</box>
</box>
<window text="Canvas Size" id="canvas_size">
<vbox>
<separator text="Size:" left="true" horizontal="true" />
<hbox>
<grid columns="2">
<label text="Width:" />
<entry text="0" id="width" maxsize="32" maxwidth="64" magnet="true" />
<label text="Height:" />
<entry text="0" id="height" maxsize="32" maxwidth="64" />
<hbox filler="true" cell_hspan="2" />
</grid>
<buttonset columns="3" id="dir">
<item icon="canvas_nw" />
<item icon="canvas_n" />
<item icon="canvas_ne" />
<item icon="canvas_w" />
<item icon="canvas_c" />
<item icon="canvas_e" />
<item icon="canvas_sw" />
<item icon="canvas_s" />
<item icon="canvas_se" />
</buttonset>
</hbox>
<separator text="Borders:" left="true" horizontal="true" />
<grid columns="4">
<label text="Left:" />
<entry text="0" id="left" maxsize="32" maxwidth="64" tooltip="Columns to be added/removed in the left side.&#10;Use a negative number to remove columns." />
<label text="Top:" />
<entry text="0" id="top" maxsize="32" maxwidth="64" tooltip="Rows to be added/removed in the top side.&#10;Use a negative number to remove rows." />
<label text="Right:" />
<entry text="0" id="right" maxsize="32" maxwidth="64" tooltip="Columns to be added/removed in the right side.&#10;Use a negative number to remove columns." />
<label text="Bottom:" />
<entry text="0" id="bottom" maxsize="32" maxwidth="64" tooltip="Rows to be added/removed in the bottom side.&#10;Use a negative number to remove rows." />
</grid>
<separator horizontal="true" />
<box horizontal="true">
<box horizontal="true" expansive="true" />
<box horizontal="true" homogeneous="true">
<hbox>
<boxfiller />
<hbox homogeneous="true">
<button text="&amp;OK" closewindow="true" id="ok" magnet="true" width="60" />
<button text="&amp;Cancel" closewindow="true" />
</box>
</box>
</box>
</hbox>
</hbox>
</vbox>
</window>
</gui>

View File

@ -29,9 +29,11 @@
#include "app/modules/editors.h"
#include "app/modules/gui.h"
#include "app/settings/settings.h"
#include "app/ui/button_set.h"
#include "app/ui/color_bar.h"
#include "app/ui/editor/editor.h"
#include "app/ui/editor/select_box_state.h"
#include "app/ui/skin/skin_theme.h"
#include "app/undo_transaction.h"
#include "base/bind.h"
#include "base/unique_ptr.h"
@ -40,11 +42,12 @@
#include "raster/sprite.h"
#include "ui/ui.h"
#include <allegro/unicode.h>
#include "generated_canvas_size.h"
namespace app {
using namespace ui;
using namespace app::skin;
// Disable warning about usage of "this" in initializer list.
#ifdef _MSC_VER
@ -52,82 +55,84 @@ using namespace ui;
#endif
// Window used to show canvas parameters.
class CanvasSizeWindow : public Window
class CanvasSizeWindow : public app::gen::CanvasSize
, public SelectBoxDelegate
{
public:
CanvasSizeWindow(int left, int top, int right, int bottom)
: Window(WithTitleBar, "Canvas Size")
, m_editor(current_editor)
, m_mainBox(app::load_widget<Widget>("canvas_size.xml", "main_box"))
, m_left(app::find_widget<Entry>(m_mainBox, "left"))
, m_right(app::find_widget<Entry>(m_mainBox, "right"))
, m_top(app::find_widget<Entry>(m_mainBox, "top"))
, m_bottom(app::find_widget<Entry>(m_mainBox, "bottom"))
, m_ok(app::find_widget<Button>(m_mainBox, "ok"))
, m_rect(-left, -top,
current_editor->sprite()->width() + left + right,
current_editor->sprite()->height() + top + bottom)
enum class Dir { NW, N, NE, W, C, E, SW, S, SE };
CanvasSizeWindow()
: m_editor(current_editor)
, m_rect(0, 0, current_editor->sprite()->width(), current_editor->sprite()->height())
, m_selectBoxState(new SelectBoxState(this, m_rect,
SelectBoxState::PaintRulers |
SelectBoxState::PaintDarkOutside))
{
addChild(m_mainBox);
SelectBoxState::PaintRulers |
SelectBoxState::PaintDarkOutside)) {
setWidth(m_rect.w);
setHeight(m_rect.h);
setLeft(0);
setRight(0);
setTop(0);
setBottom(0);
m_left->setTextf("%d", left);
m_right->setTextf("%d", right);
m_top->setTextf("%d", top);
m_bottom->setTextf("%d", bottom);
m_left ->EntryChange.connect(Bind<void>(&CanvasSizeWindow::onEntriesChange, this));
m_right ->EntryChange.connect(Bind<void>(&CanvasSizeWindow::onEntriesChange, this));
m_top ->EntryChange.connect(Bind<void>(&CanvasSizeWindow::onEntriesChange, this));
m_bottom->EntryChange.connect(Bind<void>(&CanvasSizeWindow::onEntriesChange, this));
width() ->EntryChange.connect(Bind<void>(&CanvasSizeWindow::onSizeChange, this));
height()->EntryChange.connect(Bind<void>(&CanvasSizeWindow::onSizeChange, this));
dir() ->ItemChange.connect(Bind<void>(&CanvasSizeWindow::onDirChange, this));;
left() ->EntryChange.connect(Bind<void>(&CanvasSizeWindow::onBorderChange, this));
right() ->EntryChange.connect(Bind<void>(&CanvasSizeWindow::onBorderChange, this));
top() ->EntryChange.connect(Bind<void>(&CanvasSizeWindow::onBorderChange, this));
bottom()->EntryChange.connect(Bind<void>(&CanvasSizeWindow::onBorderChange, this));
m_editor->setState(m_selectBoxState);
dir()->setSelectedItem((int)Dir::C);
updateIcons();
}
~CanvasSizeWindow()
{
~CanvasSizeWindow() {
m_editor->backToPreviousState();
}
bool pressedOk() { return getKiller() == m_ok; }
bool pressedOk() { return getKiller() == ok(); }
int getLeft() const { return m_left->getTextInt(); }
int getRight() const { return m_right->getTextInt(); }
int getTop() const { return m_top->getTextInt(); }
int getBottom() const { return m_bottom->getTextInt(); }
int getWidth() { return width()->getTextInt(); }
int getHeight() { return height()->getTextInt(); }
int getLeft() { return left()->getTextInt(); }
int getRight() { return right()->getTextInt(); }
int getTop() { return top()->getTextInt(); }
int getBottom() { return bottom()->getTextInt(); }
protected:
// SelectBoxDelegate impleentation
virtual void onChangeRectangle(const gfx::Rect& rect) override
{
virtual void onChangeRectangle(const gfx::Rect& rect) override {
m_rect = rect;
m_left->setTextf("%d", -m_rect.x);
m_top->setTextf("%d", -m_rect.y);
m_right->setTextf("%d", (m_rect.x + m_rect.w) - current_editor->sprite()->width());
m_bottom->setTextf("%d", (m_rect.y + m_rect.h) - current_editor->sprite()->height());
updateSizeFromRect();
updateBorderFromRect();
updateIcons();
}
void onEntriesChange()
{
int left = getLeft();
int top = getTop();
m_rect = gfx::Rect(-left, -top,
m_editor->sprite()->width() + left + getRight(),
m_editor->sprite()->height() + top + getBottom());
static_cast<SelectBoxState*>(m_selectBoxState.get())->setBoxBounds(m_rect);
// Redraw new rulers position
m_editor->invalidate();
void onSizeChange() {
updateBorderFromSize();
updateRectFromBorder();
updateEditorBoxFromRect();
}
virtual void onBroadcastMouseMessage(WidgetsList& targets) override
{
void onDirChange() {
updateIcons();
updateBorderFromSize();
updateRectFromBorder();
updateEditorBoxFromRect();
}
void onBorderChange() {
updateIcons();
updateRectFromBorder();
updateSizeFromRect();
updateEditorBoxFromRect();
}
virtual void onBroadcastMouseMessage(WidgetsList& targets) override {
Window::onBroadcastMouseMessage(targets);
// Add the editor as receptor of mouse events too.
@ -135,13 +140,143 @@ protected:
}
private:
void updateBorderFromSize() {
int w = getWidth() - m_editor->sprite()->width();
int h = getHeight() - m_editor->sprite()->height();
int l, r, t, b;
l = r = t = b = 0;
switch ((Dir)dir()->selectedItem()) {
case Dir::NW:
case Dir::W:
case Dir::SW:
r = w;
break;
case Dir::N:
case Dir::C:
case Dir::S:
l = r = w/2;
if (w & 1)
r += (w >= 0 ? 1: -1);
break;
case Dir::NE:
case Dir::E:
case Dir::SE:
l = w;
break;
}
switch ((Dir)dir()->selectedItem()) {
case Dir::NW:
case Dir::N:
case Dir::NE:
b = h;
break;
case Dir::W:
case Dir::C:
case Dir::E:
b = t = h/2;
if (h & 1)
t += (h >= 0 ? 1: -1);
break;
case Dir::SW:
case Dir::S:
case Dir::SE:
t = h;
break;
}
setLeft(l);
setRight(r);
setTop(t);
setBottom(b);
}
void updateRectFromBorder() {
int left = getLeft();
int top = getTop();
m_rect = gfx::Rect(-left, -top,
m_editor->sprite()->width() + left + getRight(),
m_editor->sprite()->height() + top + getBottom());
}
void updateSizeFromRect() {
setWidth(m_rect.w);
setHeight(m_rect.h);
}
void updateBorderFromRect() {
setLeft(-m_rect.x);
setTop(-m_rect.y);
setRight((m_rect.x + m_rect.w) - current_editor->sprite()->width());
setBottom((m_rect.y + m_rect.h) - current_editor->sprite()->height());
}
void updateEditorBoxFromRect() {
static_cast<SelectBoxState*>(m_selectBoxState.get())->setBoxBounds(m_rect);
// Redraw new rulers position
m_editor->invalidate();
}
void updateIcons() {
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
int l = getLeft();
int r = getRight();
int t = getTop();
int b = getBottom();
int sel = dir()->selectedItem();
int c = 0;
for (int v=0; v<3; ++v) {
for (int u=0; u<3; ++u) {
const char* iconId = "canvas_empty";
if (c == sel) {
iconId = "canvas_c";
}
else if (u+1 < 3 && (u+1)+3*v == sel) {
iconId = "canvas_w";
}
else if (u-1 >= 0 && (u-1)+3*v == sel) {
iconId = "canvas_e";
}
else if (v+1 < 3 && u+3*(v+1) == sel) {
iconId = "canvas_n";
}
else if (v-1 >= 0 && u+3*(v-1) == sel) {
iconId = "canvas_s";
}
else if (u+1 < 3 && v+1 < 3 && (u+1)+3*(v+1) == sel) {
iconId = "canvas_nw";
}
else if (u-1 >= 0 && v+1 < 3 && (u-1)+3*(v+1) == sel) {
iconId = "canvas_ne";
}
else if (u+1 < 3 && v-1 >= 0 && (u+1)+3*(v-1) == sel) {
iconId = "canvas_sw";
}
else if (u-1 >= 0 && v-1 >= 0 && (u-1)+3*(v-1) == sel) {
iconId = "canvas_se";
}
dir()->getItem(c)->setIcon(theme->get_part(iconId));
++c;
}
}
}
void setWidth(int v) { width()->setTextf("%d", v); }
void setHeight(int v) { height()->setTextf("%d", v); }
void setLeft(int v) { left()->setTextf("%d", v); }
void setRight(int v) { right()->setTextf("%d", v); }
void setTop(int v) { top()->setTextf("%d", v); }
void setBottom(int v) { bottom()->setTextf("%d", v); }
Editor* m_editor;
Widget* m_mainBox;
Entry* m_left;
Entry* m_right;
Entry* m_top;
Entry* m_bottom;
Widget* m_ok;
gfx::Rect m_rect;
EditorStatePtr m_selectBoxState;
};
@ -179,7 +314,7 @@ void CanvasSizeCommand::onExecute(Context* context)
if (context->isUiAvailable()) {
// load the window widget
base::UniquePtr<CanvasSizeWindow> window(new CanvasSizeWindow(0, 0, 0, 0));
base::UniquePtr<CanvasSizeWindow> window(new CanvasSizeWindow());
window->remapWindow();
window->centerWindow();

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -20,117 +20,178 @@
#include "config.h"
#endif
#include <allegro/unicode.h>
#include <stdarg.h>
#include "app/ui/button_set.h"
#include "base/bind.h"
#include "app/modules/gui.h"
#include "app/ui/skin/skin_theme.h"
#include "base/bind.h"
#include "gfx/color.h"
#include "she/surface.h"
#include "ui/box.h"
#include "ui/button.h"
#include "ui/graphics.h"
#include "ui/message.h"
#include "ui/paint_event.h"
#include "ui/preferred_size_event.h"
#include "ui/system.h"
#include "ui/theme.h"
#include "ui/widget.h"
#include <cstdarg>
namespace app {
using namespace ui;
using namespace app::skin;
class ButtonSet::Item : public RadioButton
WidgetType buttonset_item_type()
{
public:
Item(int index, int radioGroup, int b1, int b2, int b3, int b4)
: RadioButton("", radioGroup, kButtonWidget)
, m_index(index)
{
setRadioGroup(radioGroup);
setAlign(JI_CENTER | JI_MIDDLE);
setup_mini_look(this);
setup_bevels(this, b1, b2, b3, b4);
}
int getIndex() const { return m_index; }
private:
int m_index;
};
ButtonSet::ButtonSet(int w, int h, int firstSelected, ...)
: Box(JI_VERTICAL | JI_HOMOGENEOUS)
{
Box* hbox = NULL;
int x, y, icon;
va_list ap;
int c = 0;
char buf[256];
va_start(ap, firstSelected);
this->noBorderNoChildSpacing();
for (y=0; y<h; y++) {
if (w > 1) {
hbox = new Box(JI_HORIZONTAL | JI_HOMOGENEOUS);
hbox->noBorderNoChildSpacing();
}
for (x=0; x<w; x++) {
icon = va_arg(ap, int);
Item* item = new Item(c,
(int)(reinterpret_cast<unsigned long>(this) & 0xffffffff),
x == 0 && y == 0 ? 2: 0,
x == w-1 && y == 0 ? 2: 0,
x == 0 && y == h-1 ? 2: 0,
x == w-1 && y == h-1 ? 2: 0);
m_items.push_back(item);
usprintf(buf, "radio%d", c);
item->setId(buf);
if (icon >= 0)
set_gfxicon_to_button(item, icon, icon+1, icon, JI_CENTER | JI_MIDDLE);
item->Click.connect(Bind<void>(&ButtonSet::onItemChange, this));
if (c == firstSelected)
item->setSelected(true);
if (hbox)
hbox->addChild(item);
else
this->addChild(item);
c++;
}
if (hbox)
this->addChild(hbox);
}
va_end(ap);
static WidgetType type = kGenericWidget;
if (type == kGenericWidget)
type = register_widget_type();
return type;
}
int ButtonSet::getSelectedItem() const
ButtonSet::Item::Item()
: Widget(buttonset_item_type())
, m_icon(NULL)
{
Item* sel = findSelectedItem();
}
if (sel)
return sel->getIndex();
else
return -1;
void ButtonSet::Item::setIcon(she::Surface* icon)
{
m_icon = icon;
invalidate();
}
ButtonSet* ButtonSet::Item::buttonSet()
{
return static_cast<ButtonSet*>(getParent());
}
void ButtonSet::Item::onPaint(ui::PaintEvent& ev)
{
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
Graphics* g = ev.getGraphics();
gfx::Rect rc = getClientBounds();
gfx::Color face;
int nw;
if (!gfx::is_transparent(getBgColor()))
g->fillRect(getBgColor(), g->getClipBounds());
if (isSelected() || hasMouseOver()) {
nw = PART_TOOLBUTTON_HOT_NW;
face = theme->getColor(ThemeColor::ButtonHotFace);
}
else {
nw = PART_TOOLBUTTON_LAST_NW;
face = theme->getColor(ThemeColor::ButtonNormalFace);
}
Grid::Info info = buttonSet()->getChildInfo(this);
if (info.col < info.grid_cols-1) rc.w+=1*jguiscale();
if (info.row < info.grid_rows-1) rc.h+=3*jguiscale();
theme->draw_bounds_nw(g, rc, nw, face);
if (m_icon) {
g->drawRgbaSurface(m_icon,
rc.x + rc.w/2 - m_icon->width()/2,
rc.y + rc.h/2 - m_icon->height()/2);
}
}
bool ButtonSet::Item::onProcessMessage(ui::Message* msg)
{
switch (msg->type()) {
case ui::kMouseDownMessage:
captureMouse();
buttonSet()->setSelectedItem(this);
buttonSet()->onItemChange();
break;
case ui::kMouseUpMessage:
if (hasCapture())
releaseMouse();
break;
case ui::kMouseMoveMessage:
if (hasCapture()) {
if (buttonSet()->m_offerCapture)
offerCapture(static_cast<ui::MouseMessage*>(msg), buttonset_item_type());
}
break;
case ui::kMouseLeaveMessage:
case ui::kMouseEnterMessage:
invalidate();
break;
}
return Widget::onProcessMessage(msg);
}
void ButtonSet::Item::onPreferredSize(ui::PreferredSizeEvent& ev)
{
gfx::Size sz(16, 16); // TODO Calculate from icon
Grid::Info info = buttonSet()->getChildInfo(this);
if (info.row == info.grid_rows-1)
sz.h += 3;
ev.setPreferredSize(sz*jguiscale());
}
ButtonSet::ButtonSet(int columns)
: Grid(columns, false)
, m_offerCapture(true)
{
noBorderNoChildSpacing();
}
void ButtonSet::addItem(she::Surface* icon, int hspan, int vspan)
{
Item* item = new Item();
item->setIcon(icon);
addChildInCell(item, hspan, vspan, JI_CENTER | JI_MIDDLE);
}
ButtonSet::Item* ButtonSet::getItem(int index)
{
return dynamic_cast<Item*>(at(index));
}
int ButtonSet::selectedItem() const
{
int index = 0;
for (Widget* child : getChildren()) {
if (child->isSelected())
return index;
++index;
}
return -1;
}
void ButtonSet::setSelectedItem(int index)
{
Item* sel = findSelectedItem();
if (sel && sel->getIndex() == index)
if (index >= 0 && index < (int)getChildren().size())
setSelectedItem(static_cast<Item*>(at(index)));
else
setSelectedItem(static_cast<Item*>(NULL));
}
void ButtonSet::setSelectedItem(Item* item)
{
if (item && item->isSelected())
return;
sel->setSelected(false);
m_items[index]->setSelected(true);
Item* sel = findSelectedItem();
if (sel)
sel->setSelected(false);
if (item)
item->setSelected(true);
}
void ButtonSet::deselectItems()
@ -140,24 +201,23 @@ void ButtonSet::deselectItems()
sel->setSelected(false);
}
Widget* ButtonSet::getButtonAt(int index)
void ButtonSet::setOfferCapture(bool state)
{
return m_items[index];
}
ButtonSet::Item* ButtonSet::findSelectedItem() const
{
for (Items::const_iterator it=m_items.begin(), end=m_items.end();
it != end; ++it) {
if ((*it)->isSelected())
return *it;
}
return NULL;
m_offerCapture = state;
}
void ButtonSet::onItemChange()
{
ItemChange();
}
ButtonSet::Item* ButtonSet::findSelectedItem() const
{
for (Widget* child : getChildren()) {
if (child->isSelected())
return static_cast<Item*>(child);
}
return NULL;
}
} // namespace app

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,22 +21,38 @@
#pragma once
#include "base/signal.h"
#include "ui/box.h"
#include "ui/grid.h"
#include <string>
namespace app {
class ButtonSet : public ui::Box {
class Item;
typedef std::vector<Item*> Items;
class ButtonSet : public ui::Grid {
public:
ButtonSet(int w, int h, int firstSelected, ...);
class Item : public ui::Widget {
public:
Item();
void setIcon(she::Surface* icon);
ButtonSet* buttonSet();
protected:
void onPaint(ui::PaintEvent& ev) override;
bool onProcessMessage(ui::Message* msg) override;
void onPreferredSize(ui::PreferredSizeEvent& ev) override;
private:
she::Surface* m_icon;
};
int getSelectedItem() const;
ButtonSet(int columns);
void addItem(she::Surface* icon, int hspan = 1, int vspan = 1);
Item* getItem(int index);
int selectedItem() const;
void setSelectedItem(int index);
void setSelectedItem(Item* item);
void deselectItems();
ui::Widget* getButtonAt(int index);
void setOfferCapture(bool state);
Signal0<void> ItemChange;
@ -46,7 +62,7 @@ namespace app {
private:
Item* findSelectedItem() const;
Items m_items;
bool m_offerCapture;
};
} // namespace app

View File

@ -63,24 +63,21 @@ using namespace gfx;
using namespace ui;
using namespace tools;
class ContextBar::BrushTypeField : public Button
, public IButtonIcon {
class ContextBar::BrushTypeField : public ButtonSet {
public:
BrushTypeField()
: Button("")
: ButtonSet(1)
, m_popupWindow(NULL)
, m_brushTypeButton(NULL) {
setup_mini_look(this);
setIconInterface(this);
m_bitmap = she::instance()->createRgbaSurface(8, 8);
she::ScopedSurfaceLock lock(m_bitmap);
lock->clear();
addItem(m_bitmap);
}
~BrushTypeField() {
closePopup();
setIconInterface(NULL);
m_bitmap->dispose();
}
@ -105,43 +102,12 @@ public:
convert_image_to_surface(image, palette, m_bitmap,
0, 0, 0, 0, image->width(), image->height());
invalidate();
}
// IButtonIcon implementation
void destroy() override {
// Do nothing, BrushTypeField is added as a widget in the
// ContextBar, so it will be destroyed together with the
// ContextBar.
}
int getWidth() override {
return m_bitmap->width();
}
int getHeight() override {
return m_bitmap->height();
}
she::Surface* getNormalIcon() override {
return m_bitmap;
}
she::Surface* getSelectedIcon() override {
return m_bitmap;
}
she::Surface* getDisabledIcon() override {
return m_bitmap;
}
int getIconAlign() override {
return JI_CENTER | JI_MIDDLE;
getItem(0)->setIcon(m_bitmap);
}
protected:
void onClick(Event& ev) override {
Button::onClick(ev);
void onItemChange() override {
ButtonSet::onItemChange();
if (!m_popupWindow || !m_popupWindow->isVisible())
openPopup();
@ -150,27 +116,30 @@ protected:
}
void onPreferredSize(PreferredSizeEvent& ev) {
ev.setPreferredSize(Size(16*jguiscale(),
16*jguiscale()));
ev.setPreferredSize(Size(16, 18)*jguiscale());
}
private:
void openPopup() {
Border border = Border(2, 2, 2, 3)*jguiscale();
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
Rect rc = getBounds();
rc.y += rc.h;
rc.y += rc.h - 2*jguiscale();
rc.setSize(getPreferredSize());
rc.w *= 3;
m_popupWindow = new PopupWindow("", PopupWindow::kCloseOnClickInOtherWindow);
m_popupWindow->setAutoRemap(false);
m_popupWindow->setBorder(border);
m_popupWindow->setBounds(rc + border);
m_popupWindow->setBorder(Border(0));
m_popupWindow->setBounds(rc);
m_popupWindow->child_spacing = 0;
Region rgn(m_popupWindow->getBounds().createUnion(getBounds()));
m_popupWindow->setHotRegion(rgn);
m_brushTypeButton = new ButtonSet(3, 1, m_brushType,
PART_BRUSH_CIRCLE,
PART_BRUSH_SQUARE,
PART_BRUSH_LINE);
m_brushTypeButton = new ButtonSet(3);
m_brushTypeButton->addItem(theme->get_part(PART_BRUSH_CIRCLE));
m_brushTypeButton->addItem(theme->get_part(PART_BRUSH_SQUARE));
m_brushTypeButton->addItem(theme->get_part(PART_BRUSH_LINE));
m_brushTypeButton->setSelectedItem(m_brushType);
m_brushTypeButton->ItemChange.connect(&BrushTypeField::onBrushTypeChange, this);
m_brushTypeButton->setTransparent(true);
m_brushTypeButton->setBgColor(gfx::ColorNone);
@ -189,7 +158,7 @@ private:
}
void onBrushTypeChange() {
m_brushType = (BrushType)m_brushTypeButton->getSelectedItem();
m_brushType = (BrushType)m_brushTypeButton->selectedItem();
ISettings* settings = UIContext::instance()->settings();
Tool* currentTool = settings->getCurrentTool();
@ -550,12 +519,13 @@ protected:
}
void onPreferredSize(PreferredSizeEvent& ev) {
ev.setPreferredSize(Size(16*jguiscale(),
16*jguiscale()));
ev.setPreferredSize(Size(16, 18)*jguiscale());
}
private:
void openPopup() {
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
Border border = Border(2, 2, 2, 3)*jguiscale();
Rect rc = getBounds();
rc.y += rc.h;
@ -567,17 +537,18 @@ private:
Region rgn(m_popupWindow->getBounds().createUnion(getBounds()));
m_popupWindow->setHotRegion(rgn);
m_freehandAlgoButton = new ButtonSet(3, 1, m_freehandAlgo,
PART_FREEHAND_ALGO_DEFAULT,
PART_FREEHAND_ALGO_PIXEL_PERFECT,
PART_FREEHAND_ALGO_DOTS);
m_freehandAlgoButton = new ButtonSet(3);
m_freehandAlgoButton->addItem(theme->get_part(PART_FREEHAND_ALGO_DEFAULT));
m_freehandAlgoButton->addItem(theme->get_part(PART_FREEHAND_ALGO_PIXEL_PERFECT));
m_freehandAlgoButton->addItem(theme->get_part(PART_FREEHAND_ALGO_DOTS));
m_freehandAlgoButton->setSelectedItem((int)m_freehandAlgo);
m_freehandAlgoButton->ItemChange.connect(&FreehandAlgorithmField::onFreehandAlgoChange, this);
m_freehandAlgoButton->setTransparent(true);
m_freehandAlgoButton->setBgColor(gfx::ColorNone);
m_tooltipManager->addTooltipFor(m_freehandAlgoButton->getButtonAt(0), "Normal trace", JI_TOP);
m_tooltipManager->addTooltipFor(m_freehandAlgoButton->getButtonAt(1), "Pixel-perfect trace", JI_TOP);
m_tooltipManager->addTooltipFor(m_freehandAlgoButton->getButtonAt(2), "Dots", JI_TOP);
m_tooltipManager->addTooltipFor(at(0), "Normal trace", JI_TOP);
m_tooltipManager->addTooltipFor(at(1), "Pixel-perfect trace", JI_TOP);
m_tooltipManager->addTooltipFor(at(2), "Dots", JI_TOP);
m_popupWindow->addChild(m_freehandAlgoButton);
m_popupWindow->openWindow();
@ -657,19 +628,22 @@ protected:
class ContextBar::SelectionModeField : public ButtonSet
{
public:
SelectionModeField() : ButtonSet(3, 1, 0,
PART_SELECTION_REPLACE,
PART_SELECTION_ADD,
PART_SELECTION_SUBTRACT) {
SelectionModeField() : ButtonSet(3) {
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
addItem(theme->get_part(PART_SELECTION_REPLACE));
addItem(theme->get_part(PART_SELECTION_ADD));
addItem(theme->get_part(PART_SELECTION_SUBTRACT));
setSelectedItem(
(int)UIContext::instance()->settings()
->selection()->getSelectionMode());
}
void setupTooltips(TooltipManager* tooltipManager) {
tooltipManager->addTooltipFor(getButtonAt(0), "Replace selection", JI_BOTTOM);
tooltipManager->addTooltipFor(getButtonAt(1), "Add to selection (Shift key)", JI_BOTTOM);
tooltipManager->addTooltipFor(getButtonAt(2), "Subtract from selection (Alt key)", JI_BOTTOM);
tooltipManager->addTooltipFor(at(0), "Replace selection", JI_BOTTOM);
tooltipManager->addTooltipFor(at(1), "Add to selection (Shift key)", JI_BOTTOM);
tooltipManager->addTooltipFor(at(2), "Subtract from selection (Alt key)", JI_BOTTOM);
}
void setSelectionMode(SelectionMode mode) {
@ -681,23 +655,25 @@ protected:
void onItemChange() override {
ButtonSet::onItemChange();
int item = getSelectedItem();
UIContext::instance()->settings()->selection()
->setSelectionMode((SelectionMode)item);
->setSelectionMode((SelectionMode)selectedItem());
}
};
class ContextBar::DropPixelsField : public ButtonSet
{
public:
DropPixelsField() : ButtonSet(2, 1, -1,
PART_DROP_PIXELS_OK,
PART_DROP_PIXELS_CANCEL) {
DropPixelsField() : ButtonSet(2) {
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
addItem(theme->get_part(PART_DROP_PIXELS_OK));
addItem(theme->get_part(PART_DROP_PIXELS_CANCEL));
setOfferCapture(false);
}
void setupTooltips(TooltipManager* tooltipManager) {
tooltipManager->addTooltipFor(getButtonAt(0), "Drop pixels here", JI_BOTTOM);
tooltipManager->addTooltipFor(getButtonAt(1), "Cancel drag and drop", JI_BOTTOM);
tooltipManager->addTooltipFor(at(0), "Drop pixels here", JI_BOTTOM);
tooltipManager->addTooltipFor(at(1), "Cancel drag and drop", JI_BOTTOM);
}
Signal1<void, ContextBarObserver::DropAction> DropPixels;
@ -706,7 +682,7 @@ protected:
void onItemChange() override {
ButtonSet::onItemChange();
switch (getSelectedItem()) {
switch (selectedItem()) {
case 0: DropPixels(ContextBarObserver::DropPixels); break;
case 1: DropPixels(ContextBarObserver::CancelDrag); break;
}

View File

@ -1847,6 +1847,11 @@ void SkinTheme::paintTooltip(PaintEvent& ev)
g->drawAlignedUIString(widget->getText(), fg, bg, rc, widget->getAlign());
}
she::Surface* SkinTheme::get_part(const std::string& id) const
{
return get_part_by_id(id)->getBitmap(0);
}
gfx::Color SkinTheme::getWidgetBgColor(Widget* widget)
{
gfx::Color c = widget->getBgColor();

View File

@ -160,6 +160,7 @@ namespace app {
int get_button_selected_offset() const { return 0; } // TODO Configurable in xml
she::Surface* get_part(int part_i) const { return m_part[part_i]; }
she::Surface* get_part(const std::string& id) const;
she::Surface* get_toolicon(const char* tool_id) const;
gfx::Size get_part_size(int part_i) const;

View File

@ -24,10 +24,12 @@
#include "app/app.h"
#include "app/modules/gui.h"
#include "app/xml_document.h"
#include "app/resource_finder.h"
#include "app/ui/button_set.h"
#include "app/ui/color_button.h"
#include "app/ui/skin/skin_theme.h"
#include "app/widget_not_found.h"
#include "app/xml_document.h"
#include "app/xml_exception.h"
#include "base/bind.h"
#include "base/fs.h"
@ -44,9 +46,11 @@
namespace app {
using namespace ui;
using namespace app::skin;
static int convert_align_value_to_flags(const char *value);
static bool bool_attr_is_true(const TiXmlElement* elem, const char* attribute_name);
static int int_attr(const TiXmlElement* elem, const char* attribute_name, int default_value);
WidgetLoader::WidgetLoader()
: m_tooltipManager(NULL)
@ -105,7 +109,7 @@ Widget* WidgetLoader::loadWidgetFromXmlFile(
const char* nodename = xmlElement->Attribute("id");
if (nodename && nodename == widgetId) {
widget = convertXmlElementToWidget(xmlElement, NULL, widget);
widget = convertXmlElementToWidget(xmlElement, NULL, NULL, widget);
break;
}
@ -115,7 +119,7 @@ Widget* WidgetLoader::loadWidgetFromXmlFile(
return widget;
}
Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget* root, Widget* widget)
Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget* root, Widget* parent, Widget* widget)
{
const std::string elem_name = elem->Value();
@ -413,6 +417,29 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
if (!widget)
widget = new ColorButton(Color::fromMask(), app_get_current_pixel_format());
}
else if (elem_name == "buttonset") {
const char* columns = elem->Attribute("columns");
if (!widget && columns)
widget = new ButtonSet(strtol(columns, NULL, 10));
}
else if (elem_name == "item") {
if (!parent)
throw std::runtime_error("<item> without parent");
if (ButtonSet* buttonset = dynamic_cast<ButtonSet*>(parent)) {
SkinTheme* theme = static_cast<SkinTheme*>(parent->getTheme());
const char* icon = elem->Attribute("icon");
if (icon) {
int hspan = int_attr(elem, "hspan", 1);
int vspan = int_attr(elem, "vspan", 1);
she::Surface* sur = theme->get_part(std::string(icon));
buttonset->addItem(sur, hspan, vspan);
}
}
}
// Was the widget created?
if (widget)
@ -421,7 +448,7 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
return widget;
}
void WidgetLoader::fillWidgetWithXmlElementAttributes(const TiXmlElement* elem, ui::Widget* root, ui::Widget* widget)
void WidgetLoader::fillWidgetWithXmlElementAttributes(const TiXmlElement* elem, Widget* root, Widget* widget)
{
const char* id = elem->Attribute("id");
const char* text = elem->Attribute("text");
@ -505,7 +532,7 @@ void WidgetLoader::fillWidgetWithXmlElementAttributes(const TiXmlElement* elem,
// Children
const TiXmlElement* childElem = elem->FirstChildElement();
while (childElem) {
Widget* child = convertXmlElementToWidget(childElem, root, NULL);
Widget* child = convertXmlElementToWidget(childElem, root, widget, NULL);
if (child) {
// Attach the child in the view
if (widget->type == kViewWidget) {
@ -587,4 +614,11 @@ static bool bool_attr_is_true(const TiXmlElement* elem, const char* attribute_na
return (value != NULL) && (strcmp(value, "true") == 0);
}
static int int_attr(const TiXmlElement* elem, const char* attribute_name, int default_value)
{
const char* value = elem->Attribute(attribute_name);
return (value ? strtol(value, NULL, 10): default_value);
}
} // namespace app

View File

@ -73,7 +73,7 @@ namespace app {
const std::string& widgetId,
ui::Widget* widget);
ui::Widget* convertXmlElementToWidget(const TiXmlElement* elem, ui::Widget* root, ui::Widget* widget);
ui::Widget* convertXmlElementToWidget(const TiXmlElement* elem, ui::Widget* root, ui::Widget* parent, ui::Widget* widget);
void fillWidgetWithXmlElementAttributes(const TiXmlElement* elem, ui::Widget* root, ui::Widget* widget);
typedef std::map<std::string, IWidgetTypeCreator*> TypeCreatorsMap;

View File

@ -69,6 +69,7 @@ static std::string convert_type(const std::string& name)
{
if (name == "box") return "ui::Box";
if (name == "button") return "ui::Button";
if (name == "buttonset") return "app::ButtonSet";
if (name == "check") return "ui::CheckBox";
if (name == "combobox") return "ui::ComboBox";
if (name == "entry") return "ui::Entry";

View File

@ -96,6 +96,27 @@ void Grid::addChildInCell(Widget* child, int hspan, int vspan, int align)
}
}
Grid::Info Grid::getChildInfo(Widget* child)
{
Info info;
for (int row=0; row<(int)m_rowstrip.size(); ++row) {
for (int col=0; col<(int)m_colstrip.size(); ++col) {
Cell* cell = m_cells[row][col];
if (cell->child == child) {
info.col = col;
info.row = row;
info.hspan = cell->hspan;
info.vspan = cell->vspan;
info.grid_cols = m_colstrip.size();
info.grid_rows = m_rowstrip.size();
return info;
}
}
}
return info;
}
void Grid::onResize(ResizeEvent& ev)
{
gfx::Rect rect = ev.getBounds();
@ -151,6 +172,11 @@ void Grid::onResize(ResizeEvent& ev)
h = reqSize.h;
}
if (x+w > rect.x+rect.w-this->border_width.r)
w = rect.x+rect.w-this->border_width.r-x;
if (y+h > rect.y+rect.h-this->border_width.b)
h = rect.y+rect.h-this->border_width.b-y;
cell->child->setBounds(Rect(x, y, w, h));
}

View File

@ -14,13 +14,23 @@
namespace ui {
class Grid : public Widget
{
class Grid : public Widget {
public:
struct Info {
int col, row;
int hspan, vspan;
int grid_cols, grid_rows;
Info() : col(0), row(0),
hspan(0), vspan(0),
grid_cols(0), grid_rows(0) {
}
};
Grid(int columns, bool same_width_columns);
~Grid();
void addChildInCell(Widget* child, int hspan, int vspan, int align);
Info getChildInfo(Widget* child);
protected:
// Events

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2013 David Capello
// Copyright (C) 2001-2014 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -1237,6 +1237,23 @@ void Widget::releaseMouse()
}
}
void Widget::offerCapture(ui::MouseMessage* mouseMsg, int widget_type)
{
if (hasCapture()) {
Widget* pick = getManager()->pick(mouseMsg->position());
if (pick && pick->getType() == widget_type) {
releaseMouse();
MouseMessage* mouseMsg2 = new MouseMessage(
kMouseDownMessage,
mouseMsg->buttons(),
mouseMsg->position());
mouseMsg2->addRecipient(pick);
getManager()->enqueueMessage(mouseMsg2);
}
}
}
bool Widget::hasFocus()
{
return (this->flags & JI_HASFOCUS) ? true: false;

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2013 David Capello
// Copyright (C) 2001-2014 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -34,6 +34,7 @@ namespace ui {
class LoadLayoutEvent;
class Manager;
class Message;
class MouseMessage;
class PaintEvent;
class PreferredSizeEvent;
class ResizeEvent;
@ -180,6 +181,8 @@ namespace ui {
// Returns a list of children.
const WidgetsList& getChildren() const { return m_children; }
Widget* at(int index) { return m_children[index]; }
// Returns the first/last child or NULL if it doesn't exist.
Widget* getFirstChild() {
return (!m_children.empty() ? m_children.front(): NULL);
@ -332,11 +335,15 @@ namespace ui {
void releaseFocus();
void captureMouse();
void releaseMouse();
bool hasFocus();
bool hasMouse();
bool hasMouseOver();
bool hasCapture();
// Offer the capture to widgets of the given type
void offerCapture(ui::MouseMessage* mouseMsg, int widget_type);
// Returns lower-case letter that represet the mnemonic of the widget
// (the underscored character, i.e. the letter after & symbol).
int getMnemonicChar() const;