Implement Add/Uninstall extension (#1403)

- Added libarchive to uncompress .zip extensions
- Moved ListItem painting code to styles because we needed a selected+disabled state.
This commit is contained in:
David Capello 2017-06-12 12:38:53 -03:00
parent 950955787f
commit a9e688989f
16 changed files with 421 additions and 86 deletions

3
.gitmodules vendored
View File

@ -48,3 +48,6 @@
[submodule "third_party/json"] [submodule "third_party/json"]
path = third_party/json path = third_party/json
url = https://github.com/aseprite/json.git url = https://github.com/aseprite/json.git
[submodule "third_party/libarchive"]
path = third_party/libarchive
url = https://github.com/aseprite/libarchive.git

View File

@ -890,5 +890,14 @@
<border part="simple_color_border" /> <border part="simple_color_border" />
<border part="simple_color_selected" state="selected" /> <border part="simple_color_selected" state="selected" />
</style> </style>
<style id="list_item" border="1">
<background color="listitem_normal_face" />
<background color="listitem_selected_face" state="selected" />
<background color="face" state="disabled" />
<background color="listitem_selected_face" state="selected disabled" />
<text color="listitem_normal_text" align="left middle" x="1" />
<text color="listitem_selected_text" align="left middle" x="1" state="selected" />
<text color="disabled" align="left middle" x="1" state="disabled" />
</style>
</styles> </styles>
</theme> </theme>

View File

@ -392,7 +392,7 @@ undo_allow_nonlinear_history = Allow non-linear history
available_themes = Available Themes available_themes = Available Themes
select_theme = &Select select_theme = &Select
open_theme_folder = Open &Folder open_theme_folder = Open &Folder
new_extension = New add_extension = Add Extension
disable_extension = Disable disable_extension = Disable
uninstall_extension = Uninstall uninstall_extension = Uninstall
open_extension_folder = Open &Folder open_extension_folder = Open &Folder

View File

@ -277,7 +277,7 @@
<listbox id="extensions_list" /> <listbox id="extensions_list" />
</view> </view>
<hbox> <hbox>
<button id="new_extension" text="@.new_extension" minwidth="60" /> <button id="add_extension" text="@.add_extension" minwidth="60" />
<boxfiller /> <boxfiller />
<button id="disable_extension" text="@.disable_extension" minwidth="60" /> <button id="disable_extension" text="@.disable_extension" minwidth="60" />
<button id="uninstall_extension" text="@.uninstall_extension" minwidth="60" /> <button id="uninstall_extension" text="@.uninstall_extension" minwidth="60" />

View File

@ -527,6 +527,70 @@ ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
``` ```
# [libarchive](http://www.libarchive.org/)
```
The libarchive distribution as a whole is Copyright by Tim Kientzle
and is subject to the copyright notice reproduced at the bottom of
this file.
Each individual file in this distribution should have a clear
copyright/licensing statement at the beginning of the file. If any do
not, please let me know and I will rectify it. The following is
intended to summarize the copyright status of the individual files;
the actual statements in the files are controlling.
* Except as listed below, all C sources (including .c and .h files)
and documentation files are subject to the copyright notice reproduced
at the bottom of this file.
* The following source files are also subject in whole or in part to
a 3-clause UC Regents copyright; please read the individual source
files for details:
libarchive/archive_entry.c
libarchive/archive_read_support_filter_compress.c
libarchive/archive_write_add_filter_compress.c
libarchive/mtree.5
* The following source files are in the public domain:
libarchive/archive_getdate.c
* The build files---including Makefiles, configure scripts,
and auxiliary scripts used as part of the compile process---have
widely varying licensing terms. Please check individual files before
distributing them to see if those restrictions apply to you.
I intend for all new source code to use the license below and hope over
time to replace code with other licenses with new implementations that
do use the license below. The varying licensing of the build scripts
seems to be an unavoidable mess.
Copyright (c) 2003-2009 <author(s)>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer
in this position and unchanged.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
```
# [libjpeg](http://www.ijg.org/) # [libjpeg](http://www.ijg.org/)
``` ```

View File

@ -541,7 +541,8 @@ target_link_libraries(app-lib
${ZLIB_LIBRARIES} ${ZLIB_LIBRARIES}
${FREETYPE_LIBRARIES} ${FREETYPE_LIBRARIES}
${HARFBUZZ_LIBRARIES} ${HARFBUZZ_LIBRARIES}
taocpp-json) taocpp-json
archive_static)
if(ENABLE_SCRIPTING) if(ENABLE_SCRIPTING)
target_link_libraries(app-lib script-lib) target_link_libraries(app-lib script-lib)

View File

@ -10,8 +10,10 @@
#include "app/app.h" #include "app/app.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/console.h"
#include "app/context.h" #include "app/context.h"
#include "app/extensions.h" #include "app/extensions.h"
#include "app/file_selector.h"
#include "app/ini_file.h" #include "app/ini_file.h"
#include "app/launcher.h" #include "app/launcher.h"
#include "app/pref/preferences.h" #include "app/pref/preferences.h"
@ -72,23 +74,45 @@ class OptionsWindow : public app::gen::Options {
ExtensionItem(Extension* extension) ExtensionItem(Extension* extension)
: ListItem(extension->displayName()) : ListItem(extension->displayName())
, m_extension(extension) { , m_extension(extension) {
setEnabled(extension->isEnabled());
} }
bool isEnabled() const { return m_extension->isEnabled(); } bool isEnabled() const {
bool isInstalled() const { return m_extension->isInstalled(); } ASSERT(m_extension);
bool canBeDisabled() const { return m_extension->canBeDisabled(); } return m_extension->isEnabled();
bool canBeUninstalled() const { return m_extension->canBeUninstalled(); } }
bool isInstalled() const {
ASSERT(m_extension);
return m_extension->isInstalled();
}
bool canBeDisabled() const {
ASSERT(m_extension);
return m_extension->canBeDisabled();
}
bool canBeUninstalled() const {
ASSERT(m_extension);
return m_extension->canBeUninstalled();
}
void enable(bool state) { void enable(bool state) {
ASSERT(m_extension);
m_extension->enable(state); m_extension->enable(state);
setEnabled(m_extension->isEnabled());
} }
void uninstall() { void uninstall() {
ASSERT(m_extension);
ASSERT(canBeUninstalled()); ASSERT(canBeUninstalled());
m_extension->uninstall(); m_extension->uninstall();
m_extension = nullptr;
} }
void openFolder() const { void openFolder() const {
ASSERT(m_extension);
app::launcher::open_folder(m_extension->path()); app::launcher::open_folder(m_extension->path());
} }
@ -288,7 +312,7 @@ public:
// Extensions buttons // Extensions buttons
extensionsList()->Change.connect(base::Bind<void>(&OptionsWindow::onExtensionChange, this)); extensionsList()->Change.connect(base::Bind<void>(&OptionsWindow::onExtensionChange, this));
newExtension()->Click.connect(base::Bind<void>(&OptionsWindow::onNewExtension, this)); addExtension()->Click.connect(base::Bind<void>(&OptionsWindow::onAddExtension, this));
disableExtension()->Click.connect(base::Bind<void>(&OptionsWindow::onDisableExtension, this)); disableExtension()->Click.connect(base::Bind<void>(&OptionsWindow::onDisableExtension, this));
uninstallExtension()->Click.connect(base::Bind<void>(&OptionsWindow::onUninstallExtension, this)); uninstallExtension()->Click.connect(base::Bind<void>(&OptionsWindow::onUninstallExtension, this));
openExtensionFolder()->Click.connect(base::Bind<void>(&OptionsWindow::onOpenExtensionFolder, this)); openExtensionFolder()->Click.connect(base::Bind<void>(&OptionsWindow::onOpenExtensionFolder, this));
@ -642,8 +666,28 @@ private:
} }
} }
void onNewExtension() { void onAddExtension() {
// TODO open dialog to select a .zip file with the extension and uncompress it in the user folder FileSelectorFiles filename;
if (!app::show_file_selector(
"Add Extension", "", "zip",
FileSelectorType::Open, filename))
return;
ASSERT(!filename.empty());
try {
Extension* extension =
App::instance()->extensions().installCompressedExtension(filename.front());
// Add the new extension in the listbox
ExtensionItem* item = new ExtensionItem(extension);
extensionsList()->addChild(item);
extensionsList()->selectChild(item);
extensionsList()->layout();
}
catch (std::exception& ex) {
Console::showException(ex);
}
} }
void onDisableExtension() { void onDisableExtension() {
@ -656,9 +700,27 @@ private:
void onUninstallExtension() { void onUninstallExtension() {
ExtensionItem* item = dynamic_cast<ExtensionItem*>(extensionsList()->getSelectedChild()); ExtensionItem* item = dynamic_cast<ExtensionItem*>(extensionsList()->getSelectedChild());
if (item) { if (!item)
return;
if (ui::Alert::show(
"Warning"
"<<Do you really want to uninstall '%s' extension?"
"||&Yes||&No",
item->text().c_str()) != 1)
return;
try {
item->uninstall(); item->uninstall();
onExtensionChange();
// Remove the item from the list
extensionsList()->removeChild(item);
extensionsList()->layout();
item->deferDelete();
}
catch (std::exception& ex) {
Console::showException(ex);
} }
} }

View File

@ -10,14 +10,139 @@
#include "app/extensions.h" #include "app/extensions.h"
#include "app/ini_file.h"
#include "app/pref/preferences.h"
#include "app/resource_finder.h" #include "app/resource_finder.h"
#include "base/exception.h"
#include "base/file_handle.h"
#include "base/fs.h" #include "base/fs.h"
#include "base/unique_ptr.h" #include "base/unique_ptr.h"
#include "archive.h"
#include "archive_entry.h"
#include "tao/json.hpp" #include "tao/json.hpp"
#include <queue>
namespace app { namespace app {
namespace {
class ReadArchive {
public:
ReadArchive(const std::string& filename)
: m_arch(nullptr), m_open(false) {
m_arch = archive_read_new();
archive_read_support_format_zip(m_arch);
m_file = base::open_file(filename, "rb");
if (!m_file)
throw base::Exception("Error loading file %s",
filename.c_str());
int err;
if ((err = archive_read_open_FILE(m_arch, m_file.get())))
throw base::Exception("Error uncompressing extension\n%s (%d)",
archive_error_string(m_arch), err);
m_open = true;
}
~ReadArchive() {
if (m_arch) {
if (m_open)
archive_read_close(m_arch);
archive_read_free(m_arch);
}
}
archive_entry* readEntry() {
archive_entry* entry;
int err = archive_read_next_header(m_arch, &entry);
if (err == ARCHIVE_EOF)
return nullptr;
if (err != ARCHIVE_OK)
throw base::Exception("Error uncompressing extension\n%s",
archive_error_string(m_arch));
return entry;
}
int copyDataTo(archive* out) {
const void* buf;
size_t size;
int64_t offset;
for (;;) {
int err = archive_read_data_block(m_arch, &buf, &size, &offset);
if (err == ARCHIVE_EOF)
break;
if (err != ARCHIVE_OK)
return err;
err = archive_write_data_block(out, buf, size, offset);
if (err != ARCHIVE_OK) {
throw base::Exception("Error writing data blocks\n%s (%d)",
archive_error_string(out), err);
return err;
}
}
return ARCHIVE_OK;
}
private:
base::FileHandle m_file;
archive* m_arch;
bool m_open;
};
class WriteArchive {
public:
WriteArchive(const std::string& outputDir)
: m_arch(nullptr)
, m_open(false)
, m_outputDir(outputDir) {
m_arch = archive_write_disk_new();
m_open = true;
}
~WriteArchive() {
if (m_arch) {
if (m_open)
archive_write_close(m_arch);
archive_write_free(m_arch);
}
}
void writeEntry(ReadArchive& in, archive_entry* entry) {
const std::string origFn = archive_entry_pathname(entry);
const std::string fullFn = base::join_path(m_outputDir, origFn);
archive_entry_set_pathname(entry, fullFn.c_str());
LOG("EXT: Uncompressing file <%s> to <%s>\n",
origFn.c_str(), fullFn.c_str());
int err = archive_write_header(m_arch, entry);
if (err != ARCHIVE_OK)
throw base::Exception("Error writing file into disk\n%s (%d)",
archive_error_string(m_arch), err);
in.copyDataTo(m_arch);
err = archive_write_finish_entry(m_arch);
if (err != ARCHIVE_OK)
throw base::Exception("Error saving the last part of a file entry in disk\n%s (%d)",
archive_error_string(m_arch), err);
}
private:
archive* m_arch;
bool m_open;
std::string m_outputDir;
};
} // anonymous namespace
Extension::Extension(const std::string& path, Extension::Extension(const std::string& path,
const std::string& name, const std::string& name,
const std::string& displayName, const std::string& displayName,
@ -32,13 +157,34 @@ Extension::Extension(const std::string& path,
{ {
} }
void Extension::addTheme(const std::string& id, const std::string& path)
{
m_themes[id] = path;
}
void Extension::addPalette(const std::string& id, const std::string& path)
{
m_palettes[id] = path;
}
bool Extension::canBeDisabled() const
{
return (m_isEnabled && !isCurrentTheme());
}
bool Extension::canBeUninstalled() const
{
return (!m_isBuiltinExtension && !isCurrentTheme());
}
void Extension::enable(const bool state) void Extension::enable(const bool state)
{ {
// Do nothing // Do nothing
if (m_isEnabled == state) if (m_isEnabled == state)
return; return;
// TODO save the enable/disable state on configuration or other place set_config_bool("extensions", m_name.c_str(), state);
flush_config_file();
m_isEnabled = state; m_isEnabled = state;
Enable(this, state); Enable(this, state);
@ -49,15 +195,57 @@ void Extension::uninstall()
if (!m_isInstalled) if (!m_isInstalled)
return; return;
// TODO remove files if the extension is not built-in ASSERT(canBeUninstalled());
if (!canBeUninstalled())
return;
TRACE("EXT: Uninstall extension '%s' from '%s'...\n",
m_name.c_str(), m_path.c_str());
// Remove all files inside the extension path
uninstallFiles(m_path);
ASSERT(!base::is_directory(m_path));
m_isEnabled = false; m_isEnabled = false;
m_isInstalled = false; m_isInstalled = false;
Uninstall(this); Uninstall(this);
} }
void Extension::uninstallFiles(const std::string& path)
{
for (auto& item : base::list_files(path)) {
std::string fn = base::join_path(path, item);
if (base::is_file(fn)) {
TRACE("EXT: Deleting file '%s'\n", fn.c_str());
base::delete_file(fn);
}
else if (base::is_directory(fn)) {
uninstallFiles(fn);
}
}
TRACE("EXT: Deleting directory '%s'\n", path.c_str());
base::remove_directory(path);
}
bool Extension::isCurrentTheme() const
{
auto it = m_themes.find(Preferences::instance().theme.selected.defaultValue());
return (it != m_themes.end());
}
Extensions::Extensions() Extensions::Extensions()
{ {
// Create and get the user extensions directory
{
ResourceFinder rf2;
rf2.includeUserDir("data/extensions/.");
m_userExtensionsPath = rf2.getFirstOrCreateDefault();
m_userExtensionsPath = base::normalize_path(m_userExtensionsPath);
m_userExtensionsPath = base::get_file_path(m_userExtensionsPath);
LOG("EXT: User extensions path '%s'\n", m_userExtensionsPath.c_str());
}
ResourceFinder rf; ResourceFinder rf;
rf.includeDataDir("extensions"); rf.includeDataDir("extensions");
@ -68,22 +256,24 @@ Extensions::Extensions()
if (base::is_directory(extensionsDir)) { if (base::is_directory(extensionsDir)) {
for (auto fn : base::list_files(extensionsDir)) { for (auto fn : base::list_files(extensionsDir)) {
auto dir = base::join_path(extensionsDir, fn); const auto dir = base::join_path(extensionsDir, fn);
if (!base::is_directory(dir)) if (!base::is_directory(dir))
continue; continue;
const bool isBuiltinExtension =
(m_userExtensionsPath != base::get_file_path(dir));
auto fullFn = base::join_path(dir, "package.json"); auto fullFn = base::join_path(dir, "package.json");
fullFn = base::normalize_path(fullFn);
LOG("EXT: Loading extension '%s'...\n", fullFn.c_str()); LOG("EXT: Loading extension '%s'...\n", fullFn.c_str());
if (!base::is_file(fullFn)) { if (!base::is_file(fullFn)) {
LOG("EXT: File '%s' not found\n", fullFn.c_str()); LOG("EXT: File '%s' not found\n", fullFn.c_str());
continue; continue;
} }
bool isBuiltinExtension = true; // TODO check if the extension is in Aseprite installation folder or the user folder
try { try {
Extension* extension = loadExtension(dir, fullFn, isBuiltinExtension); loadExtension(dir, fullFn, isBuiltinExtension);
m_extensions.push_back(extension);
} }
catch (const std::exception& ex) { catch (const std::exception& ex) {
LOG("EXT: Error loading JSON file: %s\n", LOG("EXT: Error loading JSON file: %s\n",
@ -102,20 +292,46 @@ Extensions::~Extensions()
std::string Extensions::themePath(const std::string& themeId) std::string Extensions::themePath(const std::string& themeId)
{ {
auto it = m_userThemes.find(themeId); for (auto ext : m_extensions) {
if (it != m_userThemes.end()) auto it = ext->themes().find(themeId);
return it->second; if (it != ext->themes().end())
return it->second;
it = m_builtinThemes.find(themeId); }
if (it != m_builtinThemes.end())
return it->second;
return std::string(); return std::string();
} }
const std::map<std::string, std::string>& Extensions::palettes() const ExtensionItems Extensions::palettes() const
{ {
return m_palettes; ExtensionItems palettes;
for (auto ext : m_extensions)
for (auto item : ext->palettes())
palettes[item.first] = item.second;
return palettes;
}
Extension* Extensions::installCompressedExtension(const std::string& zipFn)
{
std::string dstExtensionPath =
base::join_path(m_userExtensionsPath,
base::get_file_title(zipFn));
ReadArchive in(zipFn);
WriteArchive out(dstExtensionPath);
archive_entry* entry;
while ((entry = in.readEntry()) != nullptr)
out.writeEntry(in, entry);
Extension* extension = loadExtension(
dstExtensionPath,
base::join_path(dstExtensionPath, "package.json"),
false);
if (!extension)
throw base::Exception("Error adding the new extension");
// Generate signal
NewExtension(extension);
return extension;
} }
Extension* Extensions::loadExtension(const std::string& path, Extension* Extensions::loadExtension(const std::string& path,
@ -132,7 +348,8 @@ Extension* Extensions::loadExtension(const std::string& path,
new Extension(path, new Extension(path,
name, name,
displayName, displayName,
true, // TODO check if the extension is enabled in the configuration // Extensions are enabled by default
get_config_bool("extensions", name.c_str(), true),
isBuiltinExtension)); isBuiltinExtension));
auto contributes = json["contributes"]; auto contributes = json["contributes"];
@ -151,12 +368,7 @@ Extension* Extensions::loadExtension(const std::string& path,
themeId.c_str(), themeId.c_str(),
themePath.c_str()); themePath.c_str());
if (isBuiltinExtension) { extension->addTheme(themeId, themePath);
m_builtinThemes[themeId] = themePath;
}
else {
m_userThemes[themeId] = themePath;
}
} }
} }
@ -174,11 +386,13 @@ Extension* Extensions::loadExtension(const std::string& path,
palId.c_str(), palId.c_str(),
palPath.c_str()); palPath.c_str());
m_palettes[palId] = palPath; extension->addPalette(palId, palPath);
} }
} }
} }
if (extension)
m_extensions.push_back(extension.get());
return extension.release(); return extension.release();
} }

View File

@ -16,6 +16,10 @@
namespace app { namespace app {
// Key=theme/palette/etc. id
// Value=theme/palette/etc. path
typedef std::map<std::string, std::string> ExtensionItems;
class Extension { class Extension {
public: public:
Extension(const std::string& path, Extension(const std::string& path,
@ -28,10 +32,16 @@ namespace app {
const std::string& name() const { return m_name; } const std::string& name() const { return m_name; }
const std::string& displayName() const { return m_displayName; } const std::string& displayName() const { return m_displayName; }
const ExtensionItems& themes() const { return m_themes; }
const ExtensionItems& palettes() const { return m_palettes; }
void addTheme(const std::string& id, const std::string& path);
void addPalette(const std::string& id, const std::string& path);
bool isEnabled() const { return m_isEnabled; } bool isEnabled() const { return m_isEnabled; }
bool isInstalled() const { return m_isInstalled; } bool isInstalled() const { return m_isInstalled; }
bool canBeDisabled() const { return m_isEnabled; } bool canBeDisabled() const;
bool canBeUninstalled() const { return !m_isBuiltinExtension; } bool canBeUninstalled() const;
void enable(const bool state); void enable(const bool state);
void uninstall(); void uninstall();
@ -41,6 +51,11 @@ namespace app {
obs::signal<void(Extension*)> Uninstall; obs::signal<void(Extension*)> Uninstall;
private: private:
void uninstallFiles(const std::string& path);
bool isCurrentTheme() const;
ExtensionItems m_themes;
ExtensionItems m_palettes;
std::string m_path; std::string m_path;
std::string m_name; std::string m_name;
std::string m_displayName; std::string m_displayName;
@ -64,10 +79,12 @@ namespace app {
void disableExtension(Extension* extension); void disableExtension(Extension* extension);
void uninstallExtension(Extension* extension); void uninstallExtension(Extension* extension);
void installCompressedExtension(const std::string& zipFn); Extension* installCompressedExtension(const std::string& zipFn);
std::string themePath(const std::string& themeId); std::string themePath(const std::string& themeId);
const std::map<std::string, std::string>& palettes() const; ExtensionItems palettes() const;
obs::signal<void(Extension*)> NewExtension;
private: private:
Extension* loadExtension(const std::string& path, Extension* loadExtension(const std::string& path,
@ -75,12 +92,7 @@ namespace app {
const bool isBuiltinExtension); const bool isBuiltinExtension);
List m_extensions; List m_extensions;
std::string m_userExtensionsPath;
// Key=theme id, Value=theme path
std::map<std::string, std::string> m_builtinThemes;
std::map<std::string, std::string> m_userThemes;
std::map<std::string, std::string> m_palettes;
}; };
} // namespace app } // namespace app

View File

@ -748,7 +748,7 @@ void SkinTheme::initWidget(Widget* widget)
break; break;
case kListItemWidget: case kListItemWidget:
BORDER(1 * scale); widget->setStyle(styles.listItem());
break; break;
case kComboBoxWidget: { case kComboBoxWidget: {
@ -1063,34 +1063,6 @@ void SkinTheme::paintListBox(PaintEvent& ev)
g->fillRect(colors.background(), g->getClipBounds()); g->fillRect(colors.background(), g->getClipBounds());
} }
void SkinTheme::paintListItem(ui::PaintEvent& ev)
{
Widget* widget = static_cast<Widget*>(ev.getSource());
gfx::Rect bounds = widget->clientBounds();
Graphics* g = ev.graphics();
gfx::Color fg, bg;
if (!widget->isEnabled()) {
bg = colors.face();
fg = colors.disabled();
}
else if (widget->isSelected()) {
fg = colors.listitemSelectedText();
bg = colors.listitemSelectedFace();
}
else {
fg = colors.listitemNormalText();
bg = colors.listitemNormalFace();
}
g->fillRect(bg, bounds);
if (widget->hasText()) {
bounds.shrink(widget->border());
drawText(g, nullptr, fg, bg, widget, bounds, 0, 0);
}
}
void SkinTheme::paintMenu(PaintEvent& ev) void SkinTheme::paintMenu(PaintEvent& ev)
{ {
Widget* widget = static_cast<Widget*>(ev.getSource()); Widget* widget = static_cast<Widget*>(ev.getSource());

View File

@ -59,7 +59,6 @@ namespace app {
void paintEntry(ui::PaintEvent& ev) override; void paintEntry(ui::PaintEvent& ev) override;
void paintListBox(ui::PaintEvent& ev) override; void paintListBox(ui::PaintEvent& ev) override;
void paintListItem(ui::PaintEvent& ev) override;
void paintMenu(ui::PaintEvent& ev) override; void paintMenu(ui::PaintEvent& ev) override;
void paintMenuItem(ui::PaintEvent& ev) override; void paintMenuItem(ui::PaintEvent& ev) override;
void paintSlider(ui::PaintEvent& ev) override; void paintSlider(ui::PaintEvent& ev) override;

View File

@ -1,5 +1,5 @@
// Aseprite UI Library // Aseprite UI Library
// Copyright (C) 2001-2016 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information. // Read LICENSE.txt for more information.
@ -43,11 +43,6 @@ bool ListItem::onProcessMessage(Message* msg)
return Widget::onProcessMessage(msg); return Widget::onProcessMessage(msg);
} }
void ListItem::onPaint(PaintEvent& ev)
{
theme()->paintListItem(ev);
}
void ListItem::onResize(ResizeEvent& ev) void ListItem::onResize(ResizeEvent& ev)
{ {
setBoundsQuietly(ev.bounds()); setBoundsQuietly(ev.bounds());

View File

@ -1,5 +1,5 @@
// Aseprite UI Library // Aseprite UI Library
// Copyright (C) 2001-2016 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information. // Read LICENSE.txt for more information.
@ -24,7 +24,6 @@ namespace ui {
protected: protected:
bool onProcessMessage(Message* msg) override; bool onProcessMessage(Message* msg) override;
void onPaint(PaintEvent& ev) override;
void onResize(ResizeEvent& ev) override; void onResize(ResizeEvent& ev) override;
void onSizeHint(SizeHintEvent& ev) override; void onSizeHint(SizeHintEvent& ev) override;

View File

@ -64,7 +64,6 @@ namespace ui {
virtual void paintEntry(PaintEvent& ev) = 0; virtual void paintEntry(PaintEvent& ev) = 0;
virtual void paintListBox(PaintEvent& ev) = 0; virtual void paintListBox(PaintEvent& ev) = 0;
virtual void paintListItem(PaintEvent& ev) = 0;
virtual void paintMenu(PaintEvent& ev) = 0; virtual void paintMenu(PaintEvent& ev) = 0;
virtual void paintMenuItem(PaintEvent& ev) = 0; virtual void paintMenuItem(PaintEvent& ev) = 0;
virtual void paintSlider(PaintEvent& ev) = 0; virtual void paintSlider(PaintEvent& ev) = 0;

View File

@ -111,3 +111,8 @@ endif()
# JSON # JSON
set(TAOCPP_JSON_BUILD_TESTS OFF CACHE BOOL "Build test programs") set(TAOCPP_JSON_BUILD_TESTS OFF CACHE BOOL "Build test programs")
add_subdirectory(json) add_subdirectory(json)
# libarchive
add_subdirectory(libarchive)
target_include_directories(archive_static INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/libarchive/libarchive>)

1
third_party/libarchive vendored Submodule

@ -0,0 +1 @@
Subproject commit 328453a041e2ead52bf2c64b778a29f99bd17f14