mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-15 19:52:05 +00:00
First draft-implementation of extensions (#1403)
At the moment only themes are supported and some functionality is not available yet (disabling an extension, uninstalling it, etc.)
This commit is contained in:
parent
fc7f96149f
commit
27c4d832bf
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -45,3 +45,6 @@
|
||||
[submodule "third_party/harfbuzz"]
|
||||
path = third_party/harfbuzz
|
||||
url = https://github.com/aseprite/harfbuzz.git
|
||||
[submodule "third_party/json"]
|
||||
path = third_party/json
|
||||
url = https://github.com/aseprite/json.git
|
||||
|
20
data/extensions/aseprite-theme/package.json
Normal file
20
data/extensions/aseprite-theme/package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "aseprite-theme",
|
||||
"displayName": "Aseprite Default Theme",
|
||||
"description": "Default Aseprite Pixel-Art Theme",
|
||||
"version": "1.0",
|
||||
"author": { "name": "David Capello", "email": "davidcapello@gmail.com", "url": "http://davidcapello.com/" },
|
||||
"contributors": [
|
||||
{ "name": "Ilija Melentijevic", "url": "http://ilkke.blogspot.com/" }
|
||||
],
|
||||
"publisher": "aseprite",
|
||||
"license": "CC-BY-4.0",
|
||||
"categories": [
|
||||
"Themes"
|
||||
],
|
||||
"contributes": {
|
||||
"themes": [
|
||||
{ "id": "default", "path": "." }
|
||||
]
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@ -268,6 +268,7 @@ section_grid = Grid
|
||||
section_guides_and_slices = Guides && Slices
|
||||
section_undo = Undo
|
||||
section_theme = Theme
|
||||
section_extensions = Extensions
|
||||
section_experimental = Experimental
|
||||
general = General
|
||||
screen_scaling = Screen Scaling:
|
||||
@ -391,6 +392,10 @@ undo_allow_nonlinear_history = Allow non-linear history
|
||||
available_themes = Available Themes
|
||||
select_theme = &Select
|
||||
open_theme_folder = Open &Folder
|
||||
new_extension = New
|
||||
disable_extension = Disable
|
||||
uninstall_extension = Uninstall
|
||||
open_extension_folder = Open &Folder
|
||||
user_interface = User Interface
|
||||
native_file_dialog = Use native file dialog
|
||||
flash_selected_layer = Flash layer when it is selected
|
||||
|
@ -16,6 +16,7 @@
|
||||
<listitem text="@.section_guides_and_slices" value="section_guides_and_slices" />
|
||||
<listitem text="@.section_undo" value="section_undo" />
|
||||
<listitem text="@.section_theme" value="section_theme" />
|
||||
<listitem text="@.section_extensions" value="section_extensions" />
|
||||
<listitem text="@.section_experimental" value="section_experimental" />
|
||||
</listbox>
|
||||
</view>
|
||||
@ -270,6 +271,20 @@
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<!-- Extensions -->
|
||||
<vbox id="section_extensions">
|
||||
<view expansive="true" maxsize="true">
|
||||
<listbox id="extensions_list" />
|
||||
</view>
|
||||
<hbox>
|
||||
<button id="new_extension" text="@.new_extension" minwidth="60" />
|
||||
<boxfiller />
|
||||
<button id="disable_extension" text="@.disable_extension" minwidth="60" />
|
||||
<button id="uninstall_extension" text="@.uninstall_extension" minwidth="60" />
|
||||
<button id="open_extension_folder" text="@.open_extension_folder" minwidth="60" />
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<!-- Experimental -->
|
||||
<vbox id="section_experimental">
|
||||
<separator text="@.user_interface" horizontal="true" />
|
||||
|
@ -894,6 +894,32 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
# [The Art of C++ / JSON](https://github.com/taocpp/json/)
|
||||
|
||||
```
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2017 Dr. Colin Hirsch and Daniel Frey
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
# [tinyxml](http://www.grinninglizard.com/tinyxml/)
|
||||
|
||||
```
|
||||
|
@ -363,6 +363,7 @@ add_library(app-lib
|
||||
document_range.cpp
|
||||
document_range_ops.cpp
|
||||
document_undo.cpp
|
||||
extensions.cpp
|
||||
extra_cel.cpp
|
||||
file/file.cpp
|
||||
file/file_data.cpp
|
||||
@ -539,7 +540,8 @@ target_link_libraries(app-lib
|
||||
${WEBP_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${FREETYPE_LIBRARIES}
|
||||
${HARFBUZZ_LIBRARIES})
|
||||
${HARFBUZZ_LIBRARIES}
|
||||
taocpp-json)
|
||||
|
||||
if(ENABLE_SCRIPTING)
|
||||
target_link_libraries(app-lib script-lib)
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/console.h"
|
||||
#include "app/crash/data_recovery.h"
|
||||
#include "app/extensions.h"
|
||||
#include "app/file/file.h"
|
||||
#include "app/file/file_formats_manager.h"
|
||||
#include "app/file_system.h"
|
||||
@ -96,6 +97,7 @@ public:
|
||||
RecentFiles m_recent_files;
|
||||
InputChain m_inputChain;
|
||||
clipboard::ClipboardManager m_clipboardManager;
|
||||
Extensions m_extensions;
|
||||
// This is a raw pointer because we want to delete this explicitly.
|
||||
app::crash::DataRecovery* m_recovery;
|
||||
|
||||
@ -208,7 +210,7 @@ void App::initialize(const AppOptions& options)
|
||||
ui::Manager::getDefault()->invalidate();
|
||||
}
|
||||
|
||||
// Procress options
|
||||
// Process options
|
||||
LOG("APP: Processing options...\n");
|
||||
{
|
||||
base::UniquePtr<CliDelegate> delegate;
|
||||
@ -413,6 +415,11 @@ Preferences& App::preferences() const
|
||||
return m_coreModules->m_preferences;
|
||||
}
|
||||
|
||||
Extensions& App::extensions() const
|
||||
{
|
||||
return m_modules->m_extensions;
|
||||
}
|
||||
|
||||
crash::DataRecovery* App::dataRecovery() const
|
||||
{
|
||||
return m_modules->recovery();
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -31,6 +31,7 @@ namespace app {
|
||||
class BackupIndicator;
|
||||
class ContextBar;
|
||||
class Document;
|
||||
class Extensions;
|
||||
class INotificationDelegate;
|
||||
class InputChain;
|
||||
class LegacyModules;
|
||||
@ -81,6 +82,7 @@ namespace app {
|
||||
ContextBar* contextBar() const;
|
||||
Timeline* timeline() const;
|
||||
Preferences& preferences() const;
|
||||
Extensions& extensions() const;
|
||||
crash::DataRecovery* dataRecovery() const;
|
||||
|
||||
AppBrushes& brushes() {
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "app/app.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/context.h"
|
||||
#include "app/extensions.h"
|
||||
#include "app/ini_file.h"
|
||||
#include "app/launcher.h"
|
||||
#include "app/pref/preferences.h"
|
||||
@ -34,6 +35,7 @@ namespace app {
|
||||
static const char* kSectionBgId = "section_bg";
|
||||
static const char* kSectionGridId = "section_grid";
|
||||
static const char* kSectionThemeId = "section_theme";
|
||||
static const char* kSectionExtensionsId = "section_extensions";
|
||||
|
||||
using namespace ui;
|
||||
|
||||
@ -64,6 +66,36 @@ class OptionsWindow : public app::gen::Options {
|
||||
std::string m_path;
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
class ExtensionItem : public ListItem {
|
||||
public:
|
||||
ExtensionItem(Extension* extension)
|
||||
: ListItem(extension->displayName())
|
||||
, m_extension(extension) {
|
||||
}
|
||||
|
||||
bool isEnabled() const { return m_extension->isEnabled(); }
|
||||
bool isInstalled() const { return m_extension->isInstalled(); }
|
||||
bool canBeDisabled() const { return m_extension->canBeDisabled(); }
|
||||
bool canBeUninstalled() const { return m_extension->canBeUninstalled(); }
|
||||
|
||||
void enable(bool state) {
|
||||
m_extension->enable(state);
|
||||
}
|
||||
|
||||
void uninstall() {
|
||||
ASSERT(canBeUninstalled());
|
||||
m_extension->uninstall();
|
||||
}
|
||||
|
||||
void openFolder() const {
|
||||
app::launcher::open_folder(m_extension->path());
|
||||
}
|
||||
|
||||
private:
|
||||
Extension* m_extension;
|
||||
};
|
||||
|
||||
public:
|
||||
OptionsWindow(Context* context, int& curSection)
|
||||
: m_pref(Preferences::instance())
|
||||
@ -254,6 +286,13 @@ public:
|
||||
selectTheme()->Click.connect(base::Bind<void>(&OptionsWindow::onSelectTheme, this));
|
||||
openThemeFolder()->Click.connect(base::Bind<void>(&OptionsWindow::onOpenThemeFolder, this));
|
||||
|
||||
// Extensions buttons
|
||||
extensionsList()->Change.connect(base::Bind<void>(&OptionsWindow::onExtensionChange, this));
|
||||
newExtension()->Click.connect(base::Bind<void>(&OptionsWindow::onNewExtension, this));
|
||||
disableExtension()->Click.connect(base::Bind<void>(&OptionsWindow::onDisableExtension, this));
|
||||
uninstallExtension()->Click.connect(base::Bind<void>(&OptionsWindow::onUninstallExtension, this));
|
||||
openExtensionFolder()->Click.connect(base::Bind<void>(&OptionsWindow::onOpenExtensionFolder, this));
|
||||
|
||||
// Apply button
|
||||
buttonApply()->Click.connect(base::Bind<void>(&OptionsWindow::saveConfig, this));
|
||||
|
||||
@ -408,6 +447,9 @@ private:
|
||||
// Load themes
|
||||
else if (item->getValue() == kSectionThemeId)
|
||||
loadThemes();
|
||||
// Load extension
|
||||
else if (item->getValue() == kSectionExtensionsId)
|
||||
loadExtensions();
|
||||
}
|
||||
|
||||
void onChangeBgScope() {
|
||||
@ -548,6 +590,20 @@ private:
|
||||
themeList()->layout();
|
||||
}
|
||||
|
||||
void loadExtensions() {
|
||||
// Extensions already loaded
|
||||
if (extensionsList()->getItemsCount() > 0)
|
||||
return;
|
||||
|
||||
for (auto extension : App::instance()->extensions()) {
|
||||
ExtensionItem* item = new ExtensionItem(extension);
|
||||
extensionsList()->addChild(item);
|
||||
}
|
||||
|
||||
onExtensionChange();
|
||||
extensionsList()->layout();
|
||||
}
|
||||
|
||||
void onThemeChange() {
|
||||
ThemeItem* item = dynamic_cast<ThemeItem*>(themeList()->getSelectedChild());
|
||||
selectTheme()->setEnabled(item && item->canSelect());
|
||||
@ -571,6 +627,47 @@ private:
|
||||
item->openFolder();
|
||||
}
|
||||
|
||||
void onExtensionChange() {
|
||||
ExtensionItem* item = dynamic_cast<ExtensionItem*>(extensionsList()->getSelectedChild());
|
||||
if (item && item->isInstalled()) {
|
||||
disableExtension()->setText(item->isEnabled() ? "Disable": "Enable");
|
||||
disableExtension()->setEnabled(item->isEnabled() ? item->canBeDisabled(): true);
|
||||
uninstallExtension()->setEnabled(item->canBeUninstalled());
|
||||
openExtensionFolder()->setEnabled(true);
|
||||
}
|
||||
else {
|
||||
disableExtension()->setEnabled(false);
|
||||
uninstallExtension()->setEnabled(false);
|
||||
openExtensionFolder()->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void onNewExtension() {
|
||||
// TODO open dialog to select a .zip file with the extension and uncompress it in the user folder
|
||||
}
|
||||
|
||||
void onDisableExtension() {
|
||||
ExtensionItem* item = dynamic_cast<ExtensionItem*>(extensionsList()->getSelectedChild());
|
||||
if (item) {
|
||||
item->enable(!item->isEnabled());
|
||||
onExtensionChange();
|
||||
}
|
||||
}
|
||||
|
||||
void onUninstallExtension() {
|
||||
ExtensionItem* item = dynamic_cast<ExtensionItem*>(extensionsList()->getSelectedChild());
|
||||
if (item) {
|
||||
item->uninstall();
|
||||
onExtensionChange();
|
||||
}
|
||||
}
|
||||
|
||||
void onOpenExtensionFolder() {
|
||||
ExtensionItem* item = dynamic_cast<ExtensionItem*>(extensionsList()->getSelectedChild());
|
||||
if (item)
|
||||
item->openFolder();
|
||||
}
|
||||
|
||||
void onCursorColorType() {
|
||||
switch (cursorColorType()->getSelectedItemIndex()) {
|
||||
case 0:
|
||||
|
173
src/app/extensions.cpp
Normal file
173
src/app/extensions.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/extensions.h"
|
||||
|
||||
#include "app/resource_finder.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/unique_ptr.h"
|
||||
|
||||
#include "tao/json.hpp"
|
||||
|
||||
namespace app {
|
||||
|
||||
Extension::Extension(const std::string& path,
|
||||
const std::string& name,
|
||||
const std::string& displayName,
|
||||
const bool isEnabled,
|
||||
const bool isBuiltinExtension)
|
||||
: m_path(path)
|
||||
, m_name(name)
|
||||
, m_displayName(displayName)
|
||||
, m_isEnabled(isEnabled)
|
||||
, m_isInstalled(true)
|
||||
, m_isBuiltinExtension(isBuiltinExtension)
|
||||
{
|
||||
}
|
||||
|
||||
void Extension::enable(const bool state)
|
||||
{
|
||||
// Do nothing
|
||||
if (m_isEnabled == state)
|
||||
return;
|
||||
|
||||
// TODO save the enable/disable state on configuration or other place
|
||||
|
||||
m_isEnabled = state;
|
||||
Enable(this, state);
|
||||
}
|
||||
|
||||
void Extension::uninstall()
|
||||
{
|
||||
if (!m_isInstalled)
|
||||
return;
|
||||
|
||||
// TODO remove files if the extension is not built-in
|
||||
|
||||
m_isEnabled = false;
|
||||
m_isInstalled = false;
|
||||
Uninstall(this);
|
||||
}
|
||||
|
||||
Extensions::Extensions()
|
||||
{
|
||||
ResourceFinder rf;
|
||||
rf.includeDataDir("extensions");
|
||||
|
||||
// Load extensions from data/ directory on all possible locations
|
||||
// (installed folder and user folder)
|
||||
while (rf.next()) {
|
||||
auto extensionsDir = rf.filename();
|
||||
|
||||
if (base::is_directory(extensionsDir)) {
|
||||
for (auto fn : base::list_files(extensionsDir)) {
|
||||
auto dir = base::join_path(extensionsDir, fn);
|
||||
if (!base::is_directory(dir))
|
||||
continue;
|
||||
|
||||
auto fullFn = base::join_path(dir, "package.json");
|
||||
LOG("EXT: Loading extension '%s'...\n", fullFn.c_str());
|
||||
if (!base::is_file(fullFn)) {
|
||||
LOG("EXT: File '%s' not found\n", fullFn.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isBuiltinExtension = true; // TODO check if the extension is in Aseprite installation folder or the user folder
|
||||
|
||||
try {
|
||||
Extension* extension = loadExtension(dir, fullFn, isBuiltinExtension);
|
||||
m_extensions.push_back(extension);
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
LOG("EXT: Error loading JSON file: %s\n",
|
||||
ex.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Extensions::~Extensions()
|
||||
{
|
||||
for (auto ext : m_extensions)
|
||||
delete ext;
|
||||
}
|
||||
|
||||
std::string Extensions::themePath(const std::string& themeId)
|
||||
{
|
||||
auto it = m_userThemes.find(themeId);
|
||||
if (it != m_userThemes.end())
|
||||
return it->second;
|
||||
|
||||
it = m_builtinThemes.find(themeId);
|
||||
if (it != m_builtinThemes.end())
|
||||
return it->second;
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
Extension* Extensions::loadExtension(const std::string& path,
|
||||
const std::string& fullPackageFilename,
|
||||
const bool isBuiltinExtension)
|
||||
{
|
||||
auto json = tao::json::parse_file(fullPackageFilename);
|
||||
auto name = json["name"].get_string();
|
||||
auto displayName = json["displayName"].get_string();
|
||||
|
||||
LOG("EXT: Extension '%s' loaded\n", name.c_str());
|
||||
|
||||
base::UniquePtr<Extension> extension(
|
||||
new Extension(path,
|
||||
name,
|
||||
displayName,
|
||||
true, // TODO check if the extension is enabled in the configuration
|
||||
isBuiltinExtension));
|
||||
|
||||
auto contributes = json["contributes"];
|
||||
if (contributes.is_object()) {
|
||||
auto themes = contributes["themes"];
|
||||
if (themes.is_array()) {
|
||||
for (const auto& theme : themes.get_array()) {
|
||||
auto jsonThemeId = theme.at("id");
|
||||
auto jsonThemePath = theme.at("path");
|
||||
|
||||
if (!jsonThemeId.is_string()) {
|
||||
LOG("EXT: A theme doesn't have 'id' property\n");
|
||||
}
|
||||
else if (!jsonThemePath.is_string()) {
|
||||
LOG("EXT: Theme '%s' doesn't have 'path' property\n",
|
||||
jsonThemeId.get_string().c_str());
|
||||
}
|
||||
else {
|
||||
std::string themeId = jsonThemeId.get_string();
|
||||
std::string themePath = jsonThemePath.get_string();
|
||||
|
||||
// The path must be always relative to the extension
|
||||
themePath = base::join_path(path, themePath);
|
||||
|
||||
LOG("EXT: New theme '%s' in '%s'\n",
|
||||
themeId.c_str(),
|
||||
themePath.c_str());
|
||||
|
||||
if (isBuiltinExtension) {
|
||||
m_builtinThemes[themeId] = themePath;
|
||||
}
|
||||
else {
|
||||
m_userThemes[themeId] = themePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extension.release();
|
||||
}
|
||||
|
||||
} // namespace app
|
85
src/app/extensions.h
Normal file
85
src/app/extensions.h
Normal file
@ -0,0 +1,85 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_EXTENSIONS_H_INCLUDED
|
||||
#define APP_EXTENSIONS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "obs/signal.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace app {
|
||||
|
||||
class Extension {
|
||||
public:
|
||||
Extension(const std::string& path,
|
||||
const std::string& name,
|
||||
const std::string& displayName,
|
||||
const bool isEnabled,
|
||||
const bool isBuiltinExtension);
|
||||
|
||||
const std::string& path() const { return m_path; }
|
||||
const std::string& name() const { return m_name; }
|
||||
const std::string& displayName() const { return m_displayName; }
|
||||
|
||||
bool isEnabled() const { return m_isEnabled; }
|
||||
bool isInstalled() const { return m_isInstalled; }
|
||||
bool canBeDisabled() const { return m_isEnabled; }
|
||||
bool canBeUninstalled() const { return !m_isBuiltinExtension; }
|
||||
|
||||
void enable(const bool state);
|
||||
void uninstall();
|
||||
|
||||
obs::signal<void(Extension*, bool)> Enable;
|
||||
obs::signal<void(Extension*)> Disable;
|
||||
obs::signal<void(Extension*)> Uninstall;
|
||||
|
||||
private:
|
||||
std::string m_path;
|
||||
std::string m_name;
|
||||
std::string m_displayName;
|
||||
bool m_isEnabled;
|
||||
bool m_isInstalled;
|
||||
bool m_isBuiltinExtension;
|
||||
};
|
||||
|
||||
class Extensions {
|
||||
public:
|
||||
typedef std::vector<Extension*> List;
|
||||
typedef List::iterator iterator;
|
||||
|
||||
Extensions();
|
||||
~Extensions();
|
||||
|
||||
iterator begin() { return m_extensions.begin(); }
|
||||
iterator end() { return m_extensions.end(); }
|
||||
|
||||
void enableExtension(Extension* extension);
|
||||
void disableExtension(Extension* extension);
|
||||
void uninstallExtension(Extension* extension);
|
||||
|
||||
void installCompressedExtension(const std::string& zipFn);
|
||||
|
||||
std::string themePath(const std::string& themeId);
|
||||
|
||||
private:
|
||||
Extension* loadExtension(const std::string& path,
|
||||
const std::string& fullPackageFilename,
|
||||
const bool isBuiltinExtension);
|
||||
|
||||
List m_extensions;
|
||||
|
||||
// Key=theme id, Value=theme path
|
||||
std::map<std::string, std::string> m_builtinThemes;
|
||||
std::map<std::string, std::string> m_userThemes;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -8,7 +8,11 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/console.h"
|
||||
#include "app/extensions.h"
|
||||
#include "app/font_path.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/pref/preferences.h"
|
||||
@ -18,7 +22,6 @@
|
||||
#include "app/ui/skin/font_data.h"
|
||||
#include "app/ui/skin/skin_property.h"
|
||||
#include "app/ui/skin/skin_slider_property.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/xml_document.h"
|
||||
#include "app/xml_exception.h"
|
||||
#include "base/bind.h"
|
||||
@ -48,6 +51,7 @@ namespace skin {
|
||||
using namespace gfx;
|
||||
using namespace ui;
|
||||
|
||||
// TODO For backward compatibility, in future versions we should remove this (extensions are preferred)
|
||||
const char* SkinTheme::kThemesFolderName = "themes";
|
||||
|
||||
static const char* g_cursor_names[kCursorTypes] = {
|
||||
@ -248,32 +252,31 @@ void SkinTheme::loadFontData()
|
||||
}
|
||||
}
|
||||
|
||||
void SkinTheme::loadAll(const std::string& skinId)
|
||||
void SkinTheme::loadAll(const std::string& themeId)
|
||||
{
|
||||
LOG("THEME: Loading theme %s\n", skinId.c_str());
|
||||
LOG("THEME: Loading theme %s\n", themeId.c_str());
|
||||
|
||||
if (m_fonts.empty())
|
||||
loadFontData();
|
||||
|
||||
loadSheet(skinId);
|
||||
loadXml(skinId);
|
||||
m_path = themePath(themeId);
|
||||
if (m_path.empty())
|
||||
throw base::Exception("Theme %s not found", themeId.c_str());
|
||||
|
||||
loadSheet();
|
||||
loadXml();
|
||||
}
|
||||
|
||||
void SkinTheme::loadSheet(const std::string& skinId)
|
||||
void SkinTheme::loadSheet()
|
||||
{
|
||||
// Load the skin sheet
|
||||
std::string sheet_filename(themeFileName(skinId, "sheet.png"));
|
||||
ResourceFinder rf;
|
||||
rf.includeDataDir(sheet_filename.c_str());
|
||||
if (!rf.findFirst())
|
||||
throw base::Exception("File %s not found", sheet_filename.c_str());
|
||||
|
||||
std::string sheet_filename(base::join_path(m_path, "sheet.png"));
|
||||
try {
|
||||
if (m_sheet) {
|
||||
m_sheet->dispose();
|
||||
m_sheet = nullptr;
|
||||
}
|
||||
m_sheet = she::instance()->loadRgbaSurface(rf.filename().c_str());
|
||||
m_sheet = she::instance()->loadRgbaSurface(sheet_filename.c_str());
|
||||
if (m_sheet)
|
||||
m_sheet->applyScale(guiscale());
|
||||
}
|
||||
@ -282,18 +285,14 @@ void SkinTheme::loadSheet(const std::string& skinId)
|
||||
}
|
||||
}
|
||||
|
||||
void SkinTheme::loadXml(const std::string& skinId)
|
||||
void SkinTheme::loadXml()
|
||||
{
|
||||
const int scale = guiscale();
|
||||
|
||||
// Load the skin XML
|
||||
std::string xml_filename(themeFileName(skinId, "theme.xml"));
|
||||
ResourceFinder rf;
|
||||
rf.includeDataDir(xml_filename.c_str());
|
||||
if (!rf.findFirst())
|
||||
return;
|
||||
std::string xml_filename(base::join_path(m_path, "theme.xml"));
|
||||
|
||||
XmlDocumentRef doc = open_xml(rf.filename());
|
||||
XmlDocumentRef doc = open_xml(xml_filename);
|
||||
TiXmlHandle handle(doc.get());
|
||||
|
||||
// Load fonts
|
||||
@ -304,7 +303,7 @@ void SkinTheme::loadXml(const std::string& skinId)
|
||||
.FirstChild("font").ToElement();
|
||||
while (xmlFont) {
|
||||
const char* idStr = xmlFont->Attribute("id");
|
||||
FontData* fontData = load_font(m_fonts, xmlFont, rf.filename());
|
||||
FontData* fontData = load_font(m_fonts, xmlFont, xml_filename);
|
||||
if (idStr && fontData) {
|
||||
std::string id(idStr);
|
||||
LOG(VERBOSE) << "THEME: Loading theme font '" << id << "\n";
|
||||
@ -1607,11 +1606,22 @@ void SkinTheme::paintProgressBar(ui::Graphics* g, const gfx::Rect& rc0, double p
|
||||
g->fillRect(colors.background(), gfx::Rect(rc.x+u, rc.y, rc.w-u, rc.h));
|
||||
}
|
||||
|
||||
std::string SkinTheme::themeFileName(const std::string& skinId,
|
||||
const std::string& fileName) const
|
||||
std::string SkinTheme::themePath(const std::string& themeId) const
|
||||
{
|
||||
std::string path = base::join_path(SkinTheme::kThemesFolderName, skinId);
|
||||
path = base::join_path(path, fileName);
|
||||
// First we try to find the theme on an extensions
|
||||
std::string path = App::instance()->extensions().themePath(themeId);
|
||||
if (path.empty()) {
|
||||
// Then we try a theme in the old themes/ folder
|
||||
path = base::join_path(SkinTheme::kThemesFolderName, themeId);
|
||||
path = base::join_path(path, "theme.xml");
|
||||
|
||||
ResourceFinder rf;
|
||||
rf.includeDataDir(path.c_str());
|
||||
if (!rf.findFirst())
|
||||
return std::string();
|
||||
|
||||
path = base::get_file_path(rf.filename());
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
|
@ -129,9 +129,9 @@ namespace app {
|
||||
|
||||
private:
|
||||
void loadFontData();
|
||||
void loadAll(const std::string& skinId);
|
||||
void loadSheet(const std::string& skinId);
|
||||
void loadXml(const std::string& skinId);
|
||||
void loadAll(const std::string& themeId);
|
||||
void loadSheet();
|
||||
void loadXml();
|
||||
|
||||
she::Surface* sliceSheet(she::Surface* sur, const gfx::Rect& bounds);
|
||||
gfx::Color getWidgetBgColor(ui::Widget* widget);
|
||||
@ -140,9 +140,9 @@ namespace app {
|
||||
int selected_offset, int mnemonic);
|
||||
void drawEntryText(ui::Graphics* g, ui::Entry* widget);
|
||||
|
||||
std::string themeFileName(const std::string& skinId,
|
||||
const std::string& fileName) const;
|
||||
std::string themePath(const std::string& themeId) const;
|
||||
|
||||
std::string m_path;
|
||||
she::Surface* m_sheet;
|
||||
std::map<std::string, SkinPartPtr> m_parts_by_id;
|
||||
std::map<std::string, gfx::Color> m_colors_by_id;
|
||||
|
4
third_party/CMakeLists.txt
vendored
4
third_party/CMakeLists.txt
vendored
@ -107,3 +107,7 @@ if(NOT USE_SHARED_CMARK)
|
||||
add_custom_target(copy_cmark_headers DEPENDS ${copy_cmark_headers})
|
||||
add_dependencies(libcmark_static copy_cmark_headers)
|
||||
endif()
|
||||
|
||||
# JSON
|
||||
set(TAOCPP_JSON_BUILD_TESTS OFF CACHE BOOL "Build test programs")
|
||||
add_subdirectory(json)
|
||||
|
1
third_party/json
vendored
Submodule
1
third_party/json
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 450705891c39dfc38de0ef04e239c51f1d98855d
|
Loading…
Reference in New Issue
Block a user