Merge branch 'master' into beta

This commit is contained in:
David Capello 2016-09-12 17:53:01 -03:00
parent a9f3702ba4
commit a104afc670
56 changed files with 778 additions and 235 deletions

View File

@ -18,7 +18,7 @@
<key command="ImportSpriteSheet" shortcut="Ctrl+I" mac="Cmd+I" />
<key command="ExportSpriteSheet" shortcut="Ctrl+E" mac="Cmd+E" />
<key command="RepeatLastExport" shortcut="Ctrl+Shift+X" mac="Cmd+Shift+X" />
<key command="AdvancedMode" shortcut="F11" />
<key command="AdvancedMode" shortcut="F" />
<key command="DeveloperConsole" shortcut="F12" />
<key command="Exit" win="Ctrl+Q" linux="Ctrl+Q" mac="Cmd+Q" />
<key command="Exit" win="Alt+F4" />
@ -777,7 +777,8 @@
<param name="switch" value="true" />
</item>
<item command="TogglePreview" text="Previe&amp;w" />
<item command="FullscreenPreview" text="&amp;Fullscreen Preview" />
<item command="AdvancedMode" text="&amp;Full Screen Mode" />
<item command="FullscreenPreview" text="F&amp;ull Screen Preview" />
<item command="Home" text="&amp;Home" />
<separator />
<item command="Refresh" text="&amp;Refresh &amp;&amp; Reload Skin" />

21
data/palettes/edg16.gpl Normal file
View File

@ -0,0 +1,21 @@
GIMP Palette
#
# By ENDESGA Studios
# https://twitter.com/ENDESGA
#
228 166 114 Birch
184 111 80 Oak
116 63 57 Pine
63 40 50 Darkbark
158 40 53 Blood
229 59 68 Fabric
251 146 43 Candle
255 231 98 Glow
99 198 77 Flora
50 115 69 Moss
25 61 63 Mold
79 103 129 Iron
175 191 210 Aluminium
255 255 255 White
44 232 244 Ion
4 132 209 Archaeon

37
data/palettes/edg32.gpl Normal file
View File

@ -0,0 +1,37 @@
GIMP Palette
#
# By ENDESGA Studios
# https://twitter.com/ENDESGA
#
190 74 47 Tetanus
216 118 68 Rust
234 212 170 Birch
228 166 114 Sap
184 111 80 Oak
116 63 57 Pine
63 40 50 Darkbark
158 40 53 Blood
228 59 68 Fabric
247 118 34 Amber
254 174 52 Glow
254 231 97 Light
99 199 77 Glade
62 137 72 Flora
38 92 66 Moss
25 60 62 Mold
18 78 137 Deep
0 149 233 Archaeon
44 232 245 Ion
255 255 255 White
192 203 220 Aluminium
139 155 180 Zinc
90 105 136 Iron
58 68 102 Steel
38 43 68 Shade
255 0 68 Iiem
24 20 37 Ink
104 56 108 Lilac
181 80 136 Petal
246 117 122 Peach
232 183 150 Skin
194 133 105 Shadeskin

View File

@ -86,6 +86,7 @@
<global>
<section id="general">
<option id="screen_scale" type="int" default="0" />
<option id="ui_scale" type="int" default="1" migrate="experimental.ui_scale" />
<option id="gpu_acceleration" type="bool" default="false" />
<option id="visible_timeline" type="bool" default="false" />
<option id="autoshow_timeline" type="bool" default="true" migrate="Options.AutoShowTimeline" />
@ -126,7 +127,6 @@
<option id="mini_font" type="std::string" migrate="Options.UserMiniFont" />
</section>
<section id="experimental" text="Experimental">
<option id="ui_scale" type="int" default="1" />
<option id="use_native_file_dialog" type="bool" default="false" />
<option id="flash_layer" type="bool" default="false" migrate="Options.FlashLayer" />
</section>
@ -280,6 +280,7 @@
<option id="resize_scale" type="double" default="1" />
</section>
<section id="sprite_sheet">
<option id="defined" type="bool" default="false" />
<option id="type" type="app::SpriteSheetType" default="app::SpriteSheetType::None" />
<option id="columns" type="int" default="0" />
<option id="rows" type="int" default="0" />

View File

@ -192,8 +192,8 @@
<part id="sunken_focused" x="0" y="48" w1="4" w2="4" w3="4" h1="4" h2="4" h3="4" />
<part id="sunken2_normal" x="0" y="64" w1="5" w2="6" w3="5" h1="5" h2="6" h3="5" />
<part id="sunken2_focused" x="0" y="80" w1="5" w2="6" w3="5" h1="5" h2="6" h3="5" />
<part id="sunken_mini_normal" x="16" y="64" w1="4" w2="4" w3="4" h1="4" h2="4" h3="4" />
<part id="sunken_mini_focused" x="16" y="80" w1="4" w2="4" w3="4" h1="4" h2="4" h3="4" />
<part id="sunken_mini_normal" x="16" y="64" w1="4" w2="4" w3="4" h1="3" h2="6" h3="3" />
<part id="sunken_mini_focused" x="16" y="80" w1="4" w2="4" w3="4" h1="3" h2="6" h3="3" />
<part id="window" x="0" y="0" w1="3" w2="7" w3="3" h1="15" h2="4" h3="5" />
<part id="menu" x="0" y="96" w1="3" w2="10" w3="3" h1="3" h2="9" h3="4" />
<part id="window_close_button_normal" x="16" y="0" w="9" h="11" />
@ -212,10 +212,10 @@
<part id="slider_empty" x="16" y="144" w1="5" w2="6" w3="5" h1="5" h2="5" h3="6" />
<part id="slider_full_focused" x="0" y="160" w1="5" w2="6" w3="5" h1="5" h2="5" h3="6" />
<part id="slider_empty_focused" x="16" y="160" w1="5" w2="6" w3="5" h1="5" h2="5" h3="6" />
<part id="mini_slider_full" x="32" y="144" w1="5" w2="6" w3="5" h1="5" h2="5" h3="6" />
<part id="mini_slider_empty" x="48" y="144" w1="5" w2="6" w3="5" h1="5" h2="5" h3="6" />
<part id="mini_slider_full_focused" x="32" y="160" w1="5" w2="6" w3="5" h1="5" h2="5" h3="6" />
<part id="mini_slider_empty_focused" x="48" y="160" w1="5" w2="6" w3="5" h1="5" h2="5" h3="6" />
<part id="mini_slider_full" x="32" y="144" w1="2" w2="12" w3="2" h1="2" h2="11" h3="3" />
<part id="mini_slider_empty" x="48" y="144" w1="2" w2="12" w3="2" h1="2" h2="11" h3="3" />
<part id="mini_slider_full_focused" x="32" y="160" w1="2" w2="12" w3="2" h1="2" h2="11" h3="3" />
<part id="mini_slider_empty_focused" x="48" y="160" w1="2" w2="12" w3="2" h1="2" h2="11" h3="3" />
<part id="mini_slider_thumb" x="32" y="176" w="5" h="4" />
<part id="mini_slider_thumb_focused" x="48" y="176" w="5" h="4" />
<part id="separator_horz" x="32" y="80" w="9" h="5" />

View File

@ -15,6 +15,7 @@
#include "app/commands/params.h"
#include "app/context.h"
#include "app/pref/preferences.h"
#include "app/tools/active_tool.h"
#include "app/tools/tool.h"
#include "app/ui/context_bar.h"
#include "base/convert_to.h"
@ -71,7 +72,9 @@ void ChangeBrushCommand::onLoadParams(const Params& params)
void ChangeBrushCommand::onExecute(Context* context)
{
tools::Tool* tool = App::instance()->activeTool();
// Change the brush of the selected tool in the toolbar (not the
// active tool which might be different, e.g. the quick tool)
tools::Tool* tool = App::instance()->activeToolManager()->selectedTool();
ToolPreferences::Brush& brush =
Preferences::instance().tool(tool).brush;
@ -97,7 +100,7 @@ void ChangeBrushCommand::onExecute(Context* context)
break;
case CustomBrush:
App::instance()->contextBar()
->setActiveBrushBySlot(m_slot);
->setActiveBrushBySlot(tool, m_slot);
break;
}
}

View File

@ -769,6 +769,7 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
if (!window.ok())
return;
docPref.spriteSheet.defined(true);
docPref.spriteSheet.type(window.spriteSheetTypeValue());
docPref.spriteSheet.columns(window.columnsValue());
docPref.spriteSheet.rows(window.rowsValue());
@ -790,6 +791,7 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
// Default preferences for future sprites
DocumentPreferences& defPref(Preferences::instance().document(nullptr));
defPref.spriteSheet = docPref.spriteSheet;
defPref.spriteSheet.defined(false);
if (!defPref.spriteSheet.textureFilename().empty())
defPref.spriteSheet.textureFilename.setValueAndDefault(kSpecifiedFilename);
if (!defPref.spriteSheet.dataFilename().empty())

View File

@ -168,6 +168,10 @@ private:
}
void onCommitChange() {
// Nothing to do here, as there is no layer selected.
if (!m_layer)
return;
base::ScopedValue<bool> switchSelf(m_selfUpdate, true, false);
m_timer.stop();

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -103,7 +103,8 @@ void MaskByColorCommand::onExecute(Context* context)
m_buttonColor = new ColorButton
(get_config_color("MaskColor", "Color",
ColorBar::instance()->getFgColor()),
sprite->pixelFormat());
sprite->pixelFormat(),
false);
label_tolerance = new Label("Tolerance:");
m_sliderTolerance = new Slider(0, 255, get_config_int("MaskColor", "Tolerance", 0));
m_checkPreview = new CheckBox("&Preview");

View File

@ -68,11 +68,11 @@ public:
, m_globPref(m_pref.document(nullptr))
, m_docPref(m_pref.document(context->activeDocument()))
, m_curPref(&m_docPref)
, m_checked_bg_color1(new ColorButton(app::Color::fromMask(), IMAGE_RGB))
, m_checked_bg_color2(new ColorButton(app::Color::fromMask(), IMAGE_RGB))
, m_pixelGridColor(new ColorButton(app::Color::fromMask(), IMAGE_RGB))
, m_gridColor(new ColorButton(app::Color::fromMask(), IMAGE_RGB))
, m_cursorColor(new ColorButton(m_pref.cursor.cursorColor(), IMAGE_RGB))
, m_checked_bg_color1(new ColorButton(app::Color::fromMask(), IMAGE_RGB, false))
, m_checked_bg_color2(new ColorButton(app::Color::fromMask(), IMAGE_RGB, false))
, m_pixelGridColor(new ColorButton(app::Color::fromMask(), IMAGE_RGB, false))
, m_gridColor(new ColorButton(app::Color::fromMask(), IMAGE_RGB, false))
, m_cursorColor(new ColorButton(m_pref.cursor.cursorColor(), IMAGE_RGB, false))
, m_curSection(curSection)
{
sectionListbox()->Change.connect(base::Bind<void>(&OptionsWindow::onChangeSection, this));
@ -173,7 +173,7 @@ public:
uiScale()->setSelectedItemIndex(
uiScale()->findItemIndexByValue(
base::convert_to<std::string>(m_pref.experimental.uiScale())));
base::convert_to<std::string>(m_pref.general.uiScale())));
if ((int(she::instance()->capabilities()) &
int(she::Capabilities::GpuAccelerationSwitch)) == int(she::Capabilities::GpuAccelerationSwitch)) {
@ -312,8 +312,8 @@ public:
}
int newUIScale = base::convert_to<int>(uiScale()->getValue());
if (newUIScale != m_pref.experimental.uiScale()) {
m_pref.experimental.uiScale(newUIScale);
if (newUIScale != m_pref.general.uiScale()) {
m_pref.general.uiScale(newUIScale);
warnings += "<<- UI Elements Scale";
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -51,8 +51,7 @@ void RepeatLastExportCommand::onExecute(Context* context)
DocumentPreferences& docPref =
Preferences::instance().document(document);
params.set("ui",
(docPref.spriteSheet.type() == app::SpriteSheetType::None ? "1": "0"));
params.set("ui", (docPref.spriteSheet.defined() ? "0": "1"));
}
context->executeCommand(cmd, params);

View File

@ -106,7 +106,8 @@ void SpritePropertiesCommand::onExecute(Context* context)
if (sprite->pixelFormat() == IMAGE_INDEXED) {
color_button = new ColorButton(app::Color::fromIndex(sprite->transparentColor()),
IMAGE_INDEXED);
IMAGE_INDEXED,
false);
window.transparentColorPlaceholder()->addChild(color_button);
}

View File

@ -18,6 +18,7 @@
#include "doc/cel.h"
#include "doc/file/col_file.h"
#include "doc/file/gpl_file.h"
#include "doc/file/hex_file.h"
#include "doc/file/pal_file.h"
#include "doc/image.h"
#include "doc/layer.h"
@ -33,14 +34,14 @@ using namespace doc;
std::string get_readable_palette_extensions()
{
std::string buf = get_readable_extensions();
buf += ",col,gpl,pal";
buf += ",col,gpl,hex,pal";
return buf;
}
std::string get_writable_palette_extensions()
{
std::string buf = get_writable_extensions();
buf += ",col,gpl,pal";
buf += ",col,gpl,hex,pal";
return buf;
}
@ -55,6 +56,9 @@ Palette* load_palette(const char *filename)
else if (ext == "gpl") {
pal = doc::file::load_gpl_file(filename);
}
else if (ext == "hex") {
pal = doc::file::load_hex_file(filename);
}
else if (ext == "pal") {
pal = doc::file::load_pal_file(filename);
}
@ -101,13 +105,16 @@ bool save_palette(const char *filename, const Palette* pal, int columns)
else if (ext == "gpl") {
success = doc::file::save_gpl_file(pal, filename);
}
else if (ext == "hex") {
success = doc::file::save_hex_file(pal, filename);
}
else if (ext == "pal") {
success = doc::file::save_pal_file(pal, filename);
}
else {
FileFormat* ff = FileFormatsManager::instance()->getFileFormatByExtension(ext.c_str());
if (ff && ff->support(FILE_SUPPORT_SAVE)) {
int w = (columns > 0 ? columns: pal->size());
int w = (columns > 0 ? MID(0, columns, pal->size()): pal->size());
int h = (pal->size() / w) + (pal->size() % w > 0 ? 1: 0);
app::Context tmpContext;
@ -118,18 +125,25 @@ bool save_palette(const char *filename, const Palette* pal, int columns)
Sprite* sprite = doc->sprite();
doc->sprite()->setPalette(pal, false);
Layer* layer = sprite->root()->firstLayer();
LayerImage* layer = static_cast<LayerImage*>(sprite->root()->firstLayer());
layer->configureAsBackground();
Image* image = layer->cel(frame_t(0))->image();
image->clear(0);
int x, y, c;
for (y=c=0; y<h; ++y) {
for (x=0; x<w; ++x, ++c) {
for (x=0; x<w; ++x) {
if (doc->colorMode() == doc::ColorMode::INDEXED)
image->putPixel(x, y, c);
else
image->putPixel(x, y, pal->entry(c));
if (++c == pal->size())
goto done;
}
}
done:;
doc->setFilename(filename);
success = (save_document(&tmpContext, doc) == 0);

View File

@ -179,7 +179,7 @@ int init_module_gui()
// Setup the GUI theme for all widgets
gui_theme = new SkinTheme();
gui_theme->setScale(Preferences::instance().experimental.uiScale());
gui_theme->setScale(Preferences::instance().general.uiScale());
CurrentTheme::set(gui_theme);
if (maximized)

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2015 David Capello
// Copyright (C) 2015-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -10,6 +10,7 @@
#include "app/tools/symmetries.h"
#include "app/tools/point_shape.h"
#include "app/tools/stroke.h"
#include "app/tools/tool_loop.h"
#include "doc/brush.h"
@ -20,7 +21,11 @@ namespace tools {
void HorizontalSymmetry::generateStrokes(const Stroke& mainStroke, Strokes& strokes,
ToolLoop* loop)
{
int adjust = (loop->getBrush()->bounds().w % 2);
int adjust;
if (loop->getPointShape()->isFloodFill())
adjust = 1;
else
adjust = (loop->getBrush()->bounds().w % 2);
strokes.push_back(mainStroke);
@ -33,7 +38,11 @@ void HorizontalSymmetry::generateStrokes(const Stroke& mainStroke, Strokes& stro
void VerticalSymmetry::generateStrokes(const Stroke& mainStroke, Strokes& strokes,
ToolLoop* loop)
{
int adjust = (loop->getBrush()->bounds().h % 2);
int adjust;
if (loop->getPointShape()->isFloodFill())
adjust = 1;
else
adjust = (loop->getBrush()->bounds().h % 2);
strokes.push_back(mainStroke);

View File

@ -91,11 +91,11 @@ public:
private:
void onClick() override {
ContextBar* contextBar = App::instance()->contextBar();
tools::Tool* tool = App::instance()->activeTool();
if (m_slot >= 0)
contextBar->setActiveBrushBySlot(m_slot);
contextBar->setActiveBrushBySlot(tool, m_slot);
else if (m_brush.hasBrush()) {
tools::Tool* tool = App::instance()->activeTool();
auto& brushPref = Preferences::instance().tool(tool).brush;
BrushRef brush;

View File

@ -139,8 +139,8 @@ ColorBar::ColorBar(int align)
, m_tintShadeTone(nullptr)
, m_spectrum(nullptr)
, m_wheel(nullptr)
, m_fgColor(app::Color::fromRgb(255, 255, 255), IMAGE_RGB)
, m_bgColor(app::Color::fromRgb(0, 0, 0), IMAGE_RGB)
, m_fgColor(app::Color::fromRgb(255, 255, 255), IMAGE_RGB, true)
, m_bgColor(app::Color::fromRgb(0, 0, 0), IMAGE_RGB, true)
, m_fgWarningIcon(new WarningIcon)
, m_bgWarningIcon(new WarningIcon)
, m_lock(false)
@ -183,12 +183,7 @@ ColorBar::ColorBar(int align)
setColorSelector(
Preferences::instance().colorBar.selector());
Box* buttonsBox = new HBox();
buttonsBox->addChild(&m_buttons);
m_buttons.setMaxSize(gfx::Size(m_buttons.maxSize().w,
16*ui::guiscale()));
addChild(buttonsBox);
addChild(&m_buttons);
addChild(&m_splitter);
HBox* fgBox = new HBox;
@ -202,6 +197,8 @@ ColorBar::ColorBar(int align)
addChild(fgBox);
addChild(bgBox);
m_fgColor.setId("fg_color");
m_bgColor.setId("bg_color");
m_fgColor.setExpansive(true);
m_bgColor.setExpansive(true);
@ -236,6 +233,8 @@ ColorBar::ColorBar(int align)
m_buttons.addItem(theme->parts.palSort());
m_buttons.addItem(theme->parts.palPresets());
m_buttons.addItem(theme->parts.palOptions());
m_buttons.setMaxSize(gfx::Size(m_buttons.sizeHint().w,
16*ui::guiscale()));
// Tooltips
TooltipManager* tooltipManager = new TooltipManager();

View File

@ -25,6 +25,7 @@
#include "doc/layer.h"
#include "doc/site.h"
#include "doc/sprite.h"
#include "gfx/rect_io.h"
#include "ui/size_hint_event.h"
#include "ui/ui.h"
@ -41,12 +42,15 @@ static WidgetType colorbutton_type()
return type;
}
ColorButton::ColorButton(const app::Color& color, PixelFormat pixelFormat)
ColorButton::ColorButton(const app::Color& color,
PixelFormat pixelFormat,
bool canPinSelector)
: ButtonBase("", colorbutton_type(), kButtonWidget, kButtonWidget)
, m_color(color)
, m_pixelFormat(pixelFormat)
, m_window(NULL)
, m_dependOnLayer(false)
, m_canPinSelector(canPinSelector)
{
this->setFocusStop(true);
@ -102,6 +106,13 @@ bool ColorButton::onProcessMessage(Message* msg)
{
switch (msg->type()) {
case kOpenMessage:
if (!m_windowDefaultBounds.isEmpty() &&
this->isVisible()) {
openSelectorDialog();
}
break;
case kCloseMessage:
if (m_window && m_window->isVisible())
m_window->closeWindow(NULL);
@ -233,34 +244,65 @@ void ColorButton::onClick(Event& ev)
}
}
void ColorButton::onLoadLayout(ui::LoadLayoutEvent& ev)
{
if (m_canPinSelector) {
bool pinned = false;
ev.stream() >> pinned;
if (ev.stream() && pinned)
ev.stream() >> m_windowDefaultBounds;
}
}
void ColorButton::onSaveLayout(ui::SaveLayoutEvent& ev)
{
if (m_canPinSelector && m_window && m_window->isPinned())
ev.stream() << 1 << ' ' << m_window->bounds();
else
ev.stream() << 0;
}
void ColorButton::openSelectorDialog()
{
int x, y;
bool pinned = (!m_windowDefaultBounds.isEmpty());
if (m_window == NULL) {
m_window = new ColorPopup();
m_window = new ColorPopup(m_canPinSelector);
m_window->ColorChange.connect(&ColorButton::onWindowColorChange, this);
}
if (pinned)
m_window->setPinned(true);
m_window->setColor(m_color, ColorPopup::ChangeType);
m_window->openWindow();
x = MID(0, bounds().x, ui::display_w()-m_window->bounds().w);
if (bounds().y2() <= ui::display_h()-m_window->bounds().h)
y = MAX(0, bounds().y2());
else
y = MAX(0, bounds().y-m_window->bounds().h);
m_window->positionWindow(x, y);
gfx::Rect winBounds = m_windowDefaultBounds;
if (!pinned) {
winBounds = gfx::Rect(m_window->bounds().origin(),
m_window->sizeHint());
winBounds.x = MID(0, bounds().x, ui::display_w()-winBounds.w);
if (bounds().y2() <= ui::display_h()-winBounds.h)
winBounds.y = MAX(0, bounds().y2());
else
winBounds.y = MAX(0, bounds().y-winBounds.h);
}
winBounds.x = MID(0, winBounds.x, ui::display_w()-winBounds.w);
winBounds.y = MID(0, winBounds.y, ui::display_h()-winBounds.h);
m_window->setBounds(winBounds);
m_window->manager()->dispatchMessages();
m_window->layout();
// Setup the hot-region
gfx::Rect rc = bounds().createUnion(m_window->bounds());
rc.enlarge(8);
gfx::Region rgn(rc);
static_cast<PopupWindow*>(m_window)->setHotRegion(rgn);
if (!pinned) {
gfx::Rect rc = bounds().createUnion(m_window->bounds());
rc.enlarge(8);
gfx::Region rgn(rc);
static_cast<PopupWindow*>(m_window)->setHotRegion(rgn);
}
m_windowDefaultBounds = gfx::Rect();
}
void ColorButton::closeSelectorDialog()
@ -278,6 +320,24 @@ void ColorButton::onActiveSiteChange(const Site& site)
{
if (m_dependOnLayer)
invalidate();
if (m_canPinSelector) {
// Hide window
if (!site.document()) {
if (m_window)
m_window->setVisible(false);
}
// Show window if it's pinned
else {
// Check if it's pinned from the preferences (m_windowDefaultBounds)
if (!m_window && !m_windowDefaultBounds.isEmpty())
openSelectorDialog();
// Or check if the window was hidden but it's pinned, so we've
// to show it again.
else if (m_window && m_window->isPinned())
m_window->setVisible(true);
}
}
}
} // namespace app

View File

@ -22,7 +22,9 @@ namespace app {
, public doc::ContextObserver
, public IColorSource {
public:
ColorButton(const app::Color& color, PixelFormat pixelFormat);
ColorButton(const app::Color& color,
PixelFormat pixelFormat,
bool canPinSelector);
~ColorButton();
PixelFormat pixelFormat() const;
@ -43,6 +45,8 @@ namespace app {
void onSizeHint(ui::SizeHintEvent& ev) override;
void onPaint(ui::PaintEvent& ev) override;
void onClick(ui::Event& ev) override;
void onLoadLayout(ui::LoadLayoutEvent& ev) override;
void onSaveLayout(ui::SaveLayoutEvent& ev) override;
private:
void openSelectorDialog();
@ -53,7 +57,9 @@ namespace app {
app::Color m_color;
PixelFormat m_pixelFormat;
ColorPopup* m_window;
gfx::Rect m_windowDefaultBounds;
bool m_dependOnLayer;
bool m_canPinSelector;
};
} // namespace app

View File

@ -47,7 +47,7 @@ enum {
MASK_MODE
};
ColorPopup::ColorPopup()
ColorPopup::ColorPopup(bool canPin)
: PopupWindowPin("Color Selector", ClickBehavior::CloseOnClickInOtherWindow)
, m_vbox(VERTICAL)
, m_topBox(HORIZONTAL)
@ -55,6 +55,7 @@ ColorPopup::ColorPopup()
, m_colorPalette(false, PaletteView::SelectOneColor, this, 7*guiscale())
, m_colorType(5)
, m_maskLabel("Transparent Color Selected")
, m_canPin(canPin)
, m_disableHexUpdate(false)
{
m_colorType.addItem("Index");
@ -69,6 +70,9 @@ ColorPopup::ColorPopup()
m_colorPaletteContainer.attachToView(&m_colorPalette);
m_colorPaletteContainer.setExpansive(true);
m_rgbSliders.setExpansive(true);
m_hsvSliders.setExpansive(true);
m_graySlider.setExpansive(true);
m_topBox.addChild(&m_colorType);
m_topBox.addChild(new Separator("", VERTICAL));
@ -94,6 +98,9 @@ ColorPopup::ColorPopup()
m_graySlider.ColorChange.connect(&ColorPopup::onColorSlidersChange, this);
m_hexColorEntry.ColorChange.connect(&ColorPopup::onColorHexEntryChange, this);
if (!m_canPin)
showPin(false);
selectColorType(app::Color::RgbType);
setSizeHint(gfx::Size(300*guiscale(), sizeHint().h));
@ -132,6 +139,26 @@ app::Color ColorPopup::getColor() const
return m_color;
}
void ColorPopup::onMakeFloating()
{
PopupWindowPin::onMakeFloating();
if (m_canPin) {
setSizeable(true);
setMoveable(true);
}
}
void ColorPopup::onMakeFixed()
{
PopupWindowPin::onMakeFixed();
if (m_canPin) {
setSizeable(false);
setMoveable(true);
}
}
void ColorPopup::onPaletteViewIndexChange(int index, ui::MouseButtons buttons)
{
setColorWithSignal(app::Color::fromIndex(index));

View File

@ -31,7 +31,7 @@ namespace app {
DoNotChangeType
};
ColorPopup();
ColorPopup(bool canPin);
~ColorPopup();
void setColor(const app::Color& color, SetColorOptions options);
@ -41,6 +41,8 @@ namespace app {
base::Signal1<void, const app::Color&> ColorChange;
protected:
void onMakeFloating() override;
void onMakeFixed() override;
void onColorSlidersChange(ColorSlidersChangeEvent& ev);
void onColorHexEntryChange(const app::Color& color);
void onColorTypeClick();
@ -66,6 +68,7 @@ namespace app {
GraySlider m_graySlider;
ui::Label m_maskLabel;
base::ScopedConnection m_onPaletteChangeConn;
bool m_canPin;
// This variable is used to avoid updating the m_hexColorEntry text
// when the color change is generated from a

View File

@ -68,6 +68,7 @@ namespace {
color = color_utils::color_for_ui(app::Color::fromHsv(m_color.getHue(), m_color.getSaturation(), 100 * x / w));
break;
case ColorSliders::Gray:
case ColorSliders::Alpha:
color = color_utils::color_for_ui(app::Color::fromGray(255 * x / w));
break;
}
@ -163,9 +164,9 @@ void ColorSliders::addSlider(Channel channel, const char* labelText, int min, in
box->setMaxSize(sz);
entry->setMaxSize(sz);
m_grid.addChildInCell(label, 1, 1, LEFT | MIDDLE);
m_grid.addChildInCell(box, 1, 1, HORIZONTAL | VERTICAL | EXPANSIVE);
m_grid.addChildInCell(entry, 1, 1, LEFT | MIDDLE);
m_grid.addChildInCell(label, 1, 1, LEFT | MIDDLE);
m_grid.addChildInCell(box, 1, 1, HORIZONTAL | VERTICAL);
m_grid.addChildInCell(entry, 1, 1, LEFT | MIDDLE);
}
void ColorSliders::setAbsSliderValue(int sliderIndex, int value)

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -45,7 +45,7 @@ namespace app {
base::Signal1<void, ColorSlidersChangeEvent&> ColorChange;
protected:
void onSizeHint(ui::SizeHintEvent& ev);
void onSizeHint(ui::SizeHintEvent& ev) override;
// For derived classes
void addSlider(Channel channel, const char* labelText, int min, int max);

View File

@ -886,7 +886,7 @@ class ContextBar::TransparentColorField : public HBox {
public:
TransparentColorField(ContextBar* owner)
: m_icon(1)
, m_maskColor(app::Color::fromMask(), IMAGE_RGB)
, m_maskColor(app::Color::fromMask(), IMAGE_RGB, false)
, m_owner(owner) {
SkinTheme* theme = SkinTheme::instance();
@ -1700,14 +1700,17 @@ void ContextBar::updateAutoSelectLayer(bool state)
m_autoSelectLayer->setSelected(state);
}
void ContextBar::setActiveBrushBySlot(int slot)
void ContextBar::setActiveBrushBySlot(tools::Tool* tool, int slot)
{
ASSERT(tool);
if (!tool)
return;
AppBrushes& brushes = App::instance()->brushes();
BrushSlot brush = brushes.getBrushSlot(slot);
if (!brush.isEmpty()) {
brushes.lockBrushSlot(slot);
Tool* tool = App::instance()->activeTool();
Preferences& pref = Preferences::instance();
ToolPreferences& toolPref = pref.tool(tool);
ToolPreferences::Brush& brushPref = toolPref.brush;
@ -1752,7 +1755,7 @@ void ContextBar::setActiveBrushBySlot(int slot)
tools::FreehandAlgorithm::REGULAR));
}
else {
updateForTool(App::instance()->activeTool());
updateForTool(tool);
m_brushType->showPopupAndHighlightSlot(slot);
}
}

View File

@ -54,7 +54,7 @@ namespace app {
void updateAutoSelectLayer(bool state);
void setActiveBrush(const doc::BrushRef& brush);
void setActiveBrushBySlot(int slot);
void setActiveBrushBySlot(tools::Tool* tool, int slot);
doc::BrushRef activeBrush(tools::Tool* tool = nullptr) const;
void discardActiveBrush();

View File

@ -198,11 +198,28 @@ void MovingPixelsState::onActiveToolChange(Editor* editor, tools::Tool* tool)
// If the user changed the tool when he/she is moving pixels,
// we have to drop the pixels only if the new tool is not selection...
if (m_pixelsMovement &&
(!tool->getInk(0)->isSelection() ||
!tool->getInk(1)->isSelection())) {
// We have to drop pixels
dropPixels();
if (m_pixelsMovement) {
// We don't want to drop pixels in case the user change the tool
// for scrolling/zooming/picking colors.
if ((!tool->getInk(0)->isSelection() ||
!tool->getInk(1)->isSelection()) &&
(!tool->getInk(0)->isScrollMovement() ||
!tool->getInk(1)->isScrollMovement()) &&
(!tool->getInk(0)->isZoom() ||
!tool->getInk(1)->isZoom()) &&
(!tool->getInk(0)->isEyedropper() ||
!tool->getInk(1)->isEyedropper())) {
// We have to drop pixels
dropPixels();
}
// If we've temporarily gone to a non-selection tool and now we're
// back, we've just to update the context bar to show the "moving
// pixels" controls (e.g. OK/Cancel movement buttons).
else if (tool->getInk(0)->isSelection() ||
tool->getInk(1)->isSelection()) {
ContextBar* contextBar = App::instance()->contextBar();
contextBar->updateForMovingPixels();
}
}
}
@ -477,8 +494,13 @@ void MovingPixelsState::onBeforeCommandExecution(CommandExecutionEvent& ev)
return;
}
}
// Don't drop pixels if the user zooms/scrolls/picks a color
// using commands.
else if ((command->id() == CommandId::Zoom) ||
(command->id() == CommandId::Scroll)) {
(command->id() == CommandId::Scroll) ||
(command->id() == CommandId::Eyedropper) ||
// DiscardBrush is used by Eyedropper command
(command->id() == CommandId::DiscardBrush)) {
// Do not drop pixels
return;
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -88,8 +88,8 @@ private:
}
}
void onSelect() override {
if (m_image)
void onSelect(bool selected) override {
if (!selected || m_image)
return;
ListBox* listbox = static_cast<ListBox*>(parent());

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -12,6 +12,7 @@
#include <string>
#include "app/ui/hex_color_entry.h"
#include "base/hex.h"
#include "gfx/border.h"
#include "ui/theme.h"
@ -19,13 +20,6 @@ namespace app {
using namespace ui;
static inline bool is_hex_digit(char digit)
{
return ((digit >= '0' && digit <= '9') ||
(digit >= 'a' && digit <= 'f') ||
(digit >= 'A' && digit <= 'F'));
}
HexColorEntry::HexColorEntry()
: Box(HORIZONTAL)
, m_label("#")
@ -56,7 +50,7 @@ void HexColorEntry::onEntryChange()
int r, g, b;
// Remove non hex digits
while (text.size() > 0 && !is_hex_digit(text[0]))
while (text.size() > 0 && !base::is_hex_digit(text[0]))
text.erase(0, 1);
// Fill with zeros at the end of the text

View File

@ -15,6 +15,7 @@
#include "app/commands/commands.h"
#include "app/ini_file.h"
#include "app/modules/editors.h"
#include "app/notification_delegate.h"
#include "app/pref/preferences.h"
#include "app/ui/color_bar.h"
#include "app/ui/context_bar.h"
@ -35,6 +36,7 @@
#include "app/ui/workspace_tabs.h"
#include "app/ui_context.h"
#include "base/path.h"
#include "she/display.h"
#include "ui/message.h"
#include "ui/splitter.h"
#include "ui/system.h"
@ -44,10 +46,51 @@ namespace app {
using namespace ui;
class ScreenScalePanic : public INotificationDelegate {
public:
std::string notificationText() override {
return "Reset Scale!";
}
void notificationClick() override {
auto& pref = Preferences::instance();
const int newScreenScale = 2;
const int newUIScale = 1;
bool needsRestart = false;
if (pref.general.screenScale() != newScreenScale)
pref.general.screenScale(newScreenScale);
if (pref.general.uiScale() != newUIScale) {
pref.general.uiScale(newUIScale);
needsRestart = true;
}
pref.save();
// If the UI scale is greater than 100%, we would like to avoid
// setting the Screen Scale to 200% right now to avoid a worse
// effect to the user. E.g. If the UI Scale is 400% and Screen
// Scale is 100%, and the user choose to reset the scale, we
// cannot change the Screen Scale to 200% (we're going to
// increase the problem), so we change the Screen Scale to 100%
// just to show the "restart" message.
Manager* manager = Manager::getDefault();
she::Display* display = manager->getDisplay();
display->setScale(ui::guiscale() > newUIScale ? 1: newScreenScale);
manager->setDisplay(display);
if (needsRestart)
Alert::show("Aseprite<<Restart the program.||&OK");
}
};
MainWindow::MainWindow()
: m_mode(NormalMode)
, m_homeView(nullptr)
, m_devConsoleView(nullptr)
, m_scalePanic(nullptr)
{
// Load all menus by first time.
AppMenus::instance()->reload();
@ -104,6 +147,8 @@ MainWindow::MainWindow()
MainWindow::~MainWindow()
{
delete m_scalePanic;
if (m_devConsoleView) {
if (m_devConsoleView->parent())
m_workspace->removeView(m_devConsoleView);
@ -251,6 +296,20 @@ void MainWindow::onSaveLayout(SaveLayoutEvent& ev)
Window::onSaveLayout(ev);
}
void MainWindow::onResize(ui::ResizeEvent& ev)
{
app::gen::MainWindow::onResize(ev);
she::Display* display = manager()->getDisplay();
if ((display) &&
(display->scale()*ui::guiscale() > 2) &&
(!m_scalePanic) &&
(ui::display_w()/ui::guiscale() < 320 ||
ui::display_h()/ui::guiscale() < 260)) {
showNotification(m_scalePanic = new ScreenScalePanic);
}
}
// When the active view is changed from methods like
// Workspace::splitView(), this function is called, and we have to
// inform to the UIContext that the current view has changed.

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -95,6 +95,7 @@ namespace app {
protected:
bool onProcessMessage(ui::Message* msg) override;
void onSaveLayout(ui::SaveLayoutEvent& ev) override;
void onResize(ui::ResizeEvent& ev) override;
void onActiveViewChange();
private:
@ -115,6 +116,7 @@ namespace app {
HomeView* m_homeView;
DevConsoleView* m_devConsoleView;
Notifications* m_notifications;
INotificationDelegate* m_scalePanic;
};
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -40,6 +40,19 @@ PopupWindowPin::PopupWindowPin(const std::string& text, ClickBehavior clickBehav
CENTER | MIDDLE));
}
void PopupWindowPin::showPin(bool state)
{
m_pin.setVisible(state);
}
void PopupWindowPin::setPinned(bool pinned)
{
m_pin.setSelected(pinned);
Event ev(this);
onPinClick(ev);
}
void PopupWindowPin::onPinClick(Event& ev)
{
if (m_pin.isSelected()) {
@ -57,33 +70,25 @@ bool PopupWindowPin::onProcessMessage(Message* msg)
{
switch (msg->type()) {
case kOpenMessage:
m_pin.setSelected(false);
makeFixed();
break;
case kCloseMessage:
m_pin.setSelected(false);
case kOpenMessage: {
if (!isPinned())
makeFixed();
break;
}
}
return PopupWindow::onProcessMessage(msg);
}
void PopupWindowPin::onHitTest(HitTestEvent& ev)
void PopupWindowPin::onWindowMovement()
{
PopupWindow::onHitTest(ev);
PopupWindow::onWindowMovement();
if ((m_pin.isSelected()) &&
(ev.hit() == HitTestClient)) {
if (ev.point().x <= bounds().x+2)
ev.setHit(HitTestBorderW);
else if (ev.point().x >= bounds().x2()-3)
ev.setHit(HitTestBorderE);
else
ev.setHit(HitTestCaption);
}
// If the window isn't pinned and we move it, we can automatically
// pin it.
if (!m_pin.isSelected())
setPinned(true);
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -17,9 +17,13 @@ namespace app {
public:
PopupWindowPin(const std::string& text, ClickBehavior clickBehavior);
void showPin(bool state);
bool isPinned() const { return m_pin.isSelected(); }
void setPinned(bool pinned);
protected:
virtual bool onProcessMessage(ui::Message* msg) override;
virtual void onHitTest(ui::HitTestEvent& ev) override;
virtual void onWindowMovement() override;
// The pin. Your derived class must add this pin in some place of
// the frame as a children, and you must to remove the pin from the

View File

@ -1315,21 +1315,22 @@ void SkinTheme::paintSlider(PaintEvent& ev)
g->fillRect(BGCOLOR, rc);
// Draw thumb
g->drawRgbaSurface(thumb, x-thumb->width()/2, rc.y);
int thumb_y = rc.y;
if (rc.h > thumb->height()*3)
rc.shrink(Border(0, thumb->height(), 0, 0));
// Draw borders
rc.shrink(Border(
3 * guiscale(),
thumb->height(),
3 * guiscale(),
1 * guiscale()));
drawRect(g, rc, nw.get(), gfx::ColorNone);
if (rc.h > 4*guiscale()) {
rc.shrink(Border(3, 0, 3, 1) * guiscale());
drawRect(g, rc, nw.get(), gfx::ColorNone);
}
// Draw background (using the customized ISliderBgPainter implementation)
rc.shrink(Border(1, 1, 1, 2) * guiscale());
if (!rc.isEmpty())
bgPainter->paint(widget, g, rc);
g->drawRgbaSurface(thumb, x-thumb->width()/2, thumb_y);
}
else {
// Draw borders

View File

@ -420,7 +420,7 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
}
else if (elem_name == "colorpicker") {
if (!widget)
widget = new ColorButton(Color::fromMask(), app_get_current_pixel_format());
widget = new ColorButton(Color::fromMask(), app_get_current_pixel_format(), false);
}
else if (elem_name == "dropdownbutton") {
if (!widget) {

21
src/base/hex.h Normal file
View File

@ -0,0 +1,21 @@
// Aseprite Base Library
// Copyright (c) 2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef BASE_HEX_H_INCLUDED
#define BASE_HEX_H_INCLUDED
#pragma once
namespace base {
inline bool is_hex_digit(int digit) {
return ((digit >= '0' && digit <= '9') ||
(digit >= 'a' && digit <= 'f') ||
(digit >= 'A' && digit <= 'F'));
}
} // namespace base
#endif

View File

@ -32,6 +32,7 @@ add_library(doc-lib
documents.cpp
file/col_file.cpp
file/gpl_file.cpp
file/hex_file.cpp
file/pal_file.cpp
frame_tag.cpp
frame_tag_io.cpp

87
src/doc/file/hex_file.cpp Normal file
View File

@ -0,0 +1,87 @@
// Aseprite Document Library
// Copyright (c) 2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "base/fstream_path.h"
#include "base/hex.h"
#include "base/trim_string.h"
#include "base/unique_ptr.h"
#include "doc/palette.h"
#include <cctype>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <string>
namespace doc {
namespace file {
Palette* load_hex_file(const char *filename)
{
std::ifstream f(FSTREAM_PATH(filename));
if (f.bad())
return nullptr;
base::UniquePtr<Palette> pal(new Palette(frame_t(0), 0));
// Read line by line, each line one color, ignore everything that
// doesn't look like a hex color.
std::string line;
while (std::getline(f, line)) {
// Trim line
base::trim_string(line, line);
// Remove comments
if (line.empty())
continue;
// Find 6 consecutive hex digits
for (int i=0; i<line.size(); ++i) {
int j = i;
for (; j<i+6; ++j) {
if (!base::is_hex_digit(line[j]))
break;
}
if (j-i != 6)
continue;
// Convert text (Base 16) to integer
int hex = std::strtol(line.substr(i, 6).c_str(), nullptr, 16);
int r = (hex & 0xff0000) >> 16;
int g = (hex & 0xff00) >> 8;
int b = (hex & 0xff);
pal->addEntry(rgba(r, g, b, 255));
// Done, one color per line
break;
}
}
return pal.release();
}
bool save_hex_file(const Palette *pal, const char *filename)
{
std::ofstream f(FSTREAM_PATH(filename));
if (f.bad()) return false;
f << std::hex << std::setfill('0');
for (int i=0; i<pal->size(); ++i) {
uint32_t col = pal->getEntry(i);
f << std::setw(2) << ((int)rgba_getr(col))
<< std::setw(2) << ((int)rgba_getg(col))
<< std::setw(2) << ((int)rgba_getb(col)) << "\n";
}
return true;
}
} // namespace file
} // namespace doc

23
src/doc/file/hex_file.h Normal file
View File

@ -0,0 +1,23 @@
// Aseprite Document Library
// Copyright (c) 2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef DOC_FILE_HEX_FILE_H_INCLUDED
#define DOC_FILE_HEX_FILE_H_INCLUDED
#pragma once
namespace doc {
class Palette;
namespace file {
Palette* load_hex_file(const char* filename);
bool save_hex_file(const Palette* pal, const char* filename);
} // namespace file
} // namespace doc
#endif

View File

@ -1,4 +1,4 @@
Copyright (c) 2001-2014 David Capello
Copyright (c) 2001-2016 David Capello
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@ -1,5 +1,5 @@
// Aseprite Gfx Library
// Copyright (C) 2001-2014 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -13,8 +13,7 @@
namespace gfx {
inline std::ostream& operator<<(std::ostream& os, const Rect& rect)
{
inline std::ostream& operator<<(std::ostream& os, const Rect& rect) {
return os << "("
<< rect.x << ", "
<< rect.y << ", "
@ -22,6 +21,22 @@ namespace gfx {
<< rect.h << ")";
}
inline std::istream& operator>>(std::istream& in, Rect& rect) {
while (in && in.get() != '(')
;
if (!in)
return in;
char chr;
in >> rect.x >> chr
>> rect.y >> chr
>> rect.w >> chr
>> rect.h >> chr;
return in;
}
}
#endif

View File

@ -1,5 +1,5 @@
// Aseprite Gfx Library
// Copyright (C) 2001-2013 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -10,24 +10,13 @@
#include <gtest/gtest.h>
#include "gfx/region.h"
#include "gfx/point.h"
#include "gfx/rect_io.h"
#include "gfx/region.h"
using namespace std;
using namespace gfx;
namespace gfx {
ostream& operator<<(ostream& os, const Rect& rect) {
return os << "("
<< rect.x << ", "
<< rect.y << ", "
<< rect.w << ", "
<< rect.h << ")";
}
}
ostream& operator<<(ostream& os, const Region& rgn)
{
os << "{";

View File

@ -406,9 +406,19 @@ bool is_key_pressed(KeyScancode scancode)
ev.setPreciseWheel(true);
}
else {
// Ignore the acceleration factor, just use the wheel sign.
gfx::Point pt(0, 0);
if (event.scrollingDeltaX >= 0.1)
pt.x = -1;
else if (event.scrollingDeltaX <= -0.1)
pt.x = 1;
if (event.scrollingDeltaY >= 0.1)
pt.y = -1;
else if (event.scrollingDeltaY <= -0.1)
pt.y = 1;
ev.setPointerType(she::PointerType::Mouse);
ev.setWheelDelta(gfx::Point(-event.scrollingDeltaX,
-event.scrollingDeltaY));
ev.setWheelDelta(pt);
}
queue_event(ev);

View File

@ -300,7 +300,8 @@ private:
{
NSGraphicsContext* gc = [NSGraphicsContext currentContext];
CGContextRef cg = (CGContextRef)[gc graphicsPort];
CGImageRef img = SkCreateCGImageRef(bitmap);
CGColorSpaceRef colorSpace = CGDisplayCopyColorSpace(CGMainDisplayID());
CGImageRef img = SkCreateCGImageRefWithColorspace(bitmap, colorSpace);
if (img) {
CGRect r = CGRectMake(viewBounds.origin.x+rect.x,
viewBounds.origin.y+rect.y,
@ -312,6 +313,7 @@ private:
CGContextRestoreGState(cg);
CGImageRelease(img);
}
CGColorSpaceRelease(colorSpace);
}
bitmap.unlockPixels();
}

View File

@ -1,4 +1,4 @@
Copyright (c) 2001-2015 David Capello
Copyright (c) 2001-2016 David Capello
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@ -1,4 +1,4 @@
# Aseprite UI Library
*Copyright (C) 2001-2014 David Capello*
*Copyright (C) 2001-2016 David Capello*
> Distributed under [MIT license](LICENSE.txt)

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2013, 2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -79,13 +79,13 @@ void Box::onSizeHint(SizeHintEvent& ev)
void Box::onResize(ResizeEvent& ev)
{
#define LAYOUT_CHILDREN(x, w) { \
#define LAYOUT_CHILDREN(x, y, w, h) { \
availExtraSize = availSize.w - prefSize.w; \
availSize.w -= childSpacing() * (visibleChildren-1); \
if (align() & HOMOGENEOUS) \
homogeneousSize = availSize.w / visibleChildren; \
\
Rect childPos(childrenBounds()); \
Rect defChildPos(childrenBounds()); \
int i = 0, j = 0; \
for (auto child : children()) { \
if (child->hasFlags(HIDDEN)) \
@ -111,9 +111,12 @@ void Box::onResize(ResizeEvent& ev)
} \
} \
\
childPos.w = MAX(1, size); \
Rect childPos = defChildPos; \
childPos.w = size = MID(child->minSize().w, size, child->maxSize().w); \
childPos.h = MID(child->minSize().h, childPos.h, child->maxSize().h); \
child->setBounds(childPos); \
childPos.x += size + childSpacing(); \
\
defChildPos.x += size + childSpacing(); \
availSize.w -= size; \
++i; \
} \
@ -141,10 +144,10 @@ void Box::onResize(ResizeEvent& ev)
prefSize.h -= border().height();
if (align() & HORIZONTAL) {
LAYOUT_CHILDREN(x, w);
LAYOUT_CHILDREN(x, y, w, h);
}
else {
LAYOUT_CHILDREN(y, h);
LAYOUT_CHILDREN(y, x, h, w);
}
}
}

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2013, 2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -370,9 +370,11 @@ void RadioButton::deselectRadioGroup()
}
}
void RadioButton::onSelect()
void RadioButton::onSelect(bool selected)
{
ButtonBase::onSelect();
ButtonBase::onSelect(selected);
if (!selected)
return;
if (!m_handleSelect)
return;

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2013, 2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -98,7 +98,7 @@ namespace ui {
void deselectRadioGroup();
protected:
void onSelect() override;
void onSelect(bool selected) override;
private:
int m_radioGroup;

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2013, 2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -191,20 +191,17 @@ void Grid::onResize(ResizeEvent& ev)
void Grid::onSizeHint(SizeHintEvent& ev)
{
int w, h;
w = h = 0;
calculateSize();
// Calculate the total
sumStripSize(m_colstrip, w);
sumStripSize(m_rowstrip, h);
gfx::Size sz(0, 0);
sumStripSize(m_colstrip, sz.w);
sumStripSize(m_rowstrip, sz.h);
w += border().width();
h += border().height();
sz.w += border().width();
sz.h += border().height();
ev.setSizeHint(Size(w, h));
ev.setSizeHint(sz);
}
void Grid::onPaint(PaintEvent& ev)
@ -374,7 +371,6 @@ void Grid::expandStrip(std::vector<Strip>& colstrip,
else
size = cell->h; // Transposed
}
(this->*incCol)(i, size);
}
}
@ -423,38 +419,40 @@ void Grid::distributeStripSize(std::vector<Strip>& colstrip,
}
total_req += border_size;
if (wantmore_count > 0) {
int extra_total = rect_size - total_req;
if (extra_total > 0) {
// If a expandable column-strip was empty (size=0) then we have
// to reduce the extra_total size because a new child-spacing is
// added by this column
for (i=0; i<(int)colstrip.size(); ++i) {
if ((colstrip[i].size == 0) &&
(colstrip[i].expand_count == max_expand_count || same_width)) {
extra_total -= this->childSpacing();
}
int extra_total = (rect_size - total_req);
// Expand or reduce "expandable" strip
if ((wantmore_count > 0) &&
((extra_total > 0 && (max_expand_count > 0 || same_width)) ||
(extra_total < 0))) {
// If a expandable column-strip was empty (size=0) then we have
// to reduce the extra_total size because a new child-spacing is
// added by this column
for (i=0; i<(int)colstrip.size(); ++i) {
if ((colstrip[i].size == 0) &&
(colstrip[i].expand_count == max_expand_count || same_width)) {
extra_total -= SGN(extra_total)*this->childSpacing();
}
int extra_foreach = extra_total / wantmore_count;
for (i=0; i<(int)colstrip.size(); ++i) {
if (colstrip[i].expand_count == max_expand_count || same_width) {
ASSERT(wantmore_count > 0);
colstrip[i].size += extra_foreach;
extra_total -= extra_foreach;
if (--wantmore_count == 0) {
colstrip[i].size += extra_total;
extra_total = 0;
}
}
}
ASSERT(wantmore_count == 0);
ASSERT(extra_total == 0);
}
int extra_foreach = extra_total / wantmore_count;
for (i=0; i<(int)colstrip.size(); ++i) {
if (colstrip[i].expand_count == max_expand_count || same_width) {
ASSERT(wantmore_count > 0);
colstrip[i].size += extra_foreach;
extra_total -= extra_foreach;
if (--wantmore_count == 0) {
colstrip[i].size += extra_total;
extra_total = 0;
}
}
}
ASSERT(wantmore_count == 0);
ASSERT(extra_total == 0);
}
}

View File

@ -1,11 +1,12 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Aseprite UI Library
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#define TEST_GUI
#include "tests/test.h"
#include "gfx/rect_io.h"
#include "gfx/size.h"
using namespace gfx;
@ -144,7 +145,7 @@ TEST(Grid, SameWidth2x1Grid)
// | | |
// +----------------------------+---+
//
TEST(Grid, Intrincate3x3Grid)
TEST(Grid, Intrincate2x2Grid)
{
Grid* grid = new Grid(3, false);
Widget* w1 = new Widget;
@ -226,3 +227,82 @@ TEST(Grid, FourColumns)
EXPECT_EQ(gfx::Rect(30, 0, 10, 10), d.bounds());
EXPECT_EQ(gfx::Rect(0, 10, 40, 10), e.bounds());
}
TEST(Grid, OneFixedReduceThree)
{
Grid grid(1, false);
grid.noBorderNoChildSpacing();
Widget a;
Widget b;
Widget c;
Widget d;
a.setMinSize(gfx::Size(10, 10));
b.setMinSize(gfx::Size(10, 10));
c.setMinSize(gfx::Size(10, 10));
d.setMinSize(gfx::Size(10, 10));
grid.addChildInCell(&a, 1, 1, HORIZONTAL);
grid.addChildInCell(&b, 1, 1, HORIZONTAL | VERTICAL);
grid.addChildInCell(&c, 1, 1, HORIZONTAL | VERTICAL);
grid.addChildInCell(&d, 1, 1, HORIZONTAL | VERTICAL);
// Test request size
grid.setChildSpacing(0);
EXPECT_EQ(gfx::Size(10, 40), grid.sizeHint());
// Test bigger layout
grid.setBounds(gfx::Rect(0, 0, 10, 100));
EXPECT_EQ(gfx::Rect(0, 0, 10, 10), a.bounds());
EXPECT_EQ(gfx::Rect(0, 10, 10, 30), b.bounds());
EXPECT_EQ(gfx::Rect(0, 40, 10, 30), c.bounds());
EXPECT_EQ(gfx::Rect(0, 70, 10, 30), d.bounds());
// Test perfect layout
grid.setBounds(gfx::Rect(0, 0, 10, 40));
EXPECT_EQ(gfx::Rect(0, 0, 10, 10), a.bounds());
EXPECT_EQ(gfx::Rect(0, 10, 10, 10), b.bounds());
EXPECT_EQ(gfx::Rect(0, 20, 10, 10), c.bounds());
EXPECT_EQ(gfx::Rect(0, 30, 10, 10), d.bounds());
// Test reduced layout
grid.setBounds(gfx::Rect(0, 0, 10, 16));
EXPECT_EQ(gfx::Rect(0, 0, 10, 10), a.bounds());
EXPECT_EQ(gfx::Rect(0, 10, 10, 2), b.bounds());
EXPECT_EQ(gfx::Rect(0, 12, 10, 2), c.bounds());
EXPECT_EQ(gfx::Rect(0, 14, 10, 2), d.bounds());
}
TEST(Grid, ReduceThree)
{
Grid grid(1, false);
grid.noBorderNoChildSpacing();
Widget a;
Widget b;
Widget c;
a.setMinSize(gfx::Size(10, 10));
b.setMinSize(gfx::Size(10, 10));
c.setMinSize(gfx::Size(10, 10));
grid.addChildInCell(&a, 1, 1, HORIZONTAL);
grid.addChildInCell(&b, 1, 1, HORIZONTAL);
grid.addChildInCell(&c, 1, 1, HORIZONTAL);
// Test request size
grid.setChildSpacing(0);
EXPECT_EQ(gfx::Size(10, 30), grid.sizeHint());
// Test bigger layout (as these widgets aren't expandible, they
// should have height=10)
grid.setBounds(gfx::Rect(0, 0, 10, 90));
EXPECT_EQ(gfx::Rect(0, 0, 10, 10), a.bounds());
EXPECT_EQ(gfx::Rect(0, 10, 10, 10), b.bounds());
EXPECT_EQ(gfx::Rect(0, 20, 10, 10), c.bounds());
// Test reduced layout
grid.setBounds(gfx::Rect(0, 0, 10, 12));
EXPECT_EQ(gfx::Rect(0, 0, 10, 4), a.bounds());
EXPECT_EQ(gfx::Rect(0, 4, 10, 4), b.bounds());
EXPECT_EQ(gfx::Rect(0, 8, 10, 4), c.bounds());
}

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2013, 2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -26,6 +26,7 @@ PopupWindow::PopupWindow(const std::string& text,
, m_clickBehavior(clickBehavior)
, m_enterBehavior(enterBehavior)
, m_filtering(false)
, m_fixed(false)
{
setSizeable(false);
setMoveable(false);
@ -64,12 +65,18 @@ void PopupWindow::makeFloating()
{
stopFilteringMessages();
setMoveable(true);
m_fixed = false;
onMakeFloating();
}
void PopupWindow::makeFixed()
{
startFilteringMessages();
setMoveable(false);
m_fixed = true;
onMakeFixed();
}
bool PopupWindow::onProcessMessage(Message* msg)
@ -91,7 +98,7 @@ bool PopupWindow::onProcessMessage(Message* msg)
break;
case kMouseLeaveMessage:
if (m_hotRegion.isEmpty() && !isMoveable())
if (m_hotRegion.isEmpty() && m_fixed)
closeWindow(nullptr);
break;
@ -139,7 +146,7 @@ bool PopupWindow::onProcessMessage(Message* msg)
break;
case kMouseMoveMessage:
if (!isMoveable() &&
if (m_fixed &&
!m_hotRegion.isEmpty() &&
manager()->getCapture() == NULL) {
gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position();
@ -202,19 +209,34 @@ void PopupWindow::onInitTheme(InitThemeEvent& ev)
void PopupWindow::onHitTest(HitTestEvent& ev)
{
Window::onHitTest(ev);
Widget* picked = manager()->pick(ev.point());
if (picked) {
WidgetType type = picked->type();
if ((type == kWindowWidget && picked == this) ||
type == kBoxWidget ||
type == kLabelWidget ||
type == kGridWidget ||
type == kSeparatorWidget) {
ev.setHit(HitTestCaption);
return;
if (type == kWindowWidget && picked == this) {
if (isSizeable() && (ev.hit() == HitTestBorderNW ||
ev.hit() == HitTestBorderN ||
ev.hit() == HitTestBorderNE ||
ev.hit() == HitTestBorderE ||
ev.hit() == HitTestBorderSE ||
ev.hit() == HitTestBorderS ||
ev.hit() == HitTestBorderSW ||
ev.hit() == HitTestBorderW)) {
// Use the hit value from Window::onHitTest()
return;
}
else {
ev.setHit(isMoveable() ? HitTestCaption: HitTestClient);
}
}
else if (type == kBoxWidget ||
type == kLabelWidget ||
type == kGridWidget ||
type == kSeparatorWidget) {
ev.setHit(isMoveable() ? HitTestCaption: HitTestClient);
}
}
Window::onHitTest(ev);
}
void PopupWindow::startFilteringMessages()
@ -241,4 +263,14 @@ void PopupWindow::stopFilteringMessages()
}
}
void PopupWindow::onMakeFloating()
{
// Do nothing
}
void PopupWindow::onMakeFixed()
{
// Do nothing
}
} // namespace ui

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2013, 2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -46,6 +46,9 @@ namespace ui {
void onInitTheme(InitThemeEvent& ev) override;
void onHitTest(HitTestEvent& ev) override;
virtual void onMakeFloating();
virtual void onMakeFixed();
private:
void startFilteringMessages();
void stopFilteringMessages();
@ -54,6 +57,7 @@ namespace ui {
EnterBehavior m_enterBehavior;
gfx::Region m_hotRegion;
bool m_filtering;
bool m_fixed;
};
} // namespace ui

View File

@ -211,7 +211,7 @@ void Widget::setEnabled(bool state)
disableFlags(DISABLED);
invalidate();
onEnable();
onEnable(true);
}
}
else {
@ -221,7 +221,7 @@ void Widget::setEnabled(bool state)
enableFlags(DISABLED);
invalidate();
onDisable();
onEnable(false);
}
}
}
@ -233,7 +233,7 @@ void Widget::setSelected(bool state)
enableFlags(SELECTED);
invalidate();
onSelect();
onSelect(true);
}
}
else {
@ -241,7 +241,7 @@ void Widget::setSelected(bool state)
disableFlags(SELECTED);
invalidate();
onDeselect();
onSelect(false);
}
}
}
@ -1436,22 +1436,12 @@ void Widget::onSetDecorativeWidgetBounds()
}
}
void Widget::onEnable()
void Widget::onEnable(bool enabled)
{
// Do nothing
}
void Widget::onDisable()
{
// Do nothing
}
void Widget::onSelect()
{
// Do nothing
}
void Widget::onDeselect()
void Widget::onSelect(bool selected)
{
// Do nothing
}

View File

@ -368,10 +368,8 @@ namespace ui {
virtual void onBroadcastMouseMessage(WidgetsList& targets);
virtual void onInitTheme(InitThemeEvent& ev);
virtual void onSetDecorativeWidgetBounds();
virtual void onEnable();
virtual void onDisable();
virtual void onSelect();
virtual void onDeselect();
virtual void onEnable(bool enabled);
virtual void onSelect(bool selected);
virtual void onSetText();
virtual void onSetBgColor();

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -182,6 +182,11 @@ void Window::onWindowResize()
// Do nothing
}
void Window::onWindowMovement()
{
// Do nothing
}
void Window::remapWindow()
{
if (m_isAutoRemap) {
@ -579,6 +584,8 @@ void Window::moveWindow(const gfx::Rect& rect, bool use_blit)
}
manager->invalidateDisplayRegion(invalidManagerRegion);
onWindowMovement();
}
} // namespace ui

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2013, 2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -46,6 +46,7 @@ namespace ui {
bool isDesktop() const { return m_isDesktop; }
bool isOnTop() const { return m_isOnTop; }
bool isWantFocus() const { return m_isWantFocus; }
bool isSizeable() const { return m_isSizeable; }
bool isMoveable() const { return m_isMoveable; }
HitTest hitTest(const gfx::Point& point);
@ -67,6 +68,7 @@ namespace ui {
virtual void onClose(CloseEvent& ev);
virtual void onHitTest(HitTestEvent& ev);
virtual void onWindowResize();
virtual void onWindowMovement();
private:
void windowSetPosition(const gfx::Rect& rect);