mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-30 15:32:38 +00:00
parent
0429282967
commit
0e0b83b478
@ -829,6 +829,7 @@ section_experimental = Experimental
|
||||
general = General
|
||||
screen_scaling = Screen Scaling:
|
||||
ui_scaling = UI Elements Scaling:
|
||||
language = Language:
|
||||
gpu_acceleration = GPU acceleration
|
||||
gpu_acceleration_tooltip = Check this option to enable hardware acceleration
|
||||
show_menu_bar = Show Aseprite menu bar
|
||||
|
@ -28,24 +28,25 @@
|
||||
<!-- General -->
|
||||
<vbox id="section_general">
|
||||
<separator text="@.section_general" horizontal="true" />
|
||||
<hbox>
|
||||
<grid columns="2">
|
||||
<label text="@.screen_scaling" />
|
||||
<combobox id="screen_scale">
|
||||
<listitem text="100%" value="1" />
|
||||
<listitem text="200%" value="2" />
|
||||
<listitem text="300%" value="3" />
|
||||
<listitem text="400%" value="4" />
|
||||
</combobox>
|
||||
<label text="@.ui_scaling" />
|
||||
<combobox id="ui_scale">
|
||||
<listitem text="100%" value="1" />
|
||||
<listitem text="200%" value="2" />
|
||||
<listitem text="300%" value="3" />
|
||||
<listitem text="400%" value="4" />
|
||||
</combobox>
|
||||
</grid>
|
||||
</hbox>
|
||||
<grid columns="2">
|
||||
<label text="@.screen_scaling" />
|
||||
<combobox id="screen_scale">
|
||||
<listitem text="100%" value="1" />
|
||||
<listitem text="200%" value="2" />
|
||||
<listitem text="300%" value="3" />
|
||||
<listitem text="400%" value="4" />
|
||||
</combobox>
|
||||
<label text="@.ui_scaling" />
|
||||
<combobox id="ui_scale">
|
||||
<listitem text="100%" value="1" />
|
||||
<listitem text="200%" value="2" />
|
||||
<listitem text="300%" value="3" />
|
||||
<listitem text="400%" value="4" />
|
||||
</combobox>
|
||||
|
||||
<label text="@.language" />
|
||||
<combobox id="language" />
|
||||
</grid>
|
||||
<check id="gpu_acceleration"
|
||||
text="@.gpu_acceleration"
|
||||
tooltip="@.gpu_acceleration_tooltip" />
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "app/file/file_formats_manager.h"
|
||||
#include "app/file_system.h"
|
||||
#include "app/gui_xml.h"
|
||||
#include "app/i18n/strings.h"
|
||||
#include "app/ini_file.h"
|
||||
#include "app/log.h"
|
||||
#include "app/modules.h"
|
||||
@ -193,8 +194,10 @@ void App::initialize(const AppOptions& options)
|
||||
if (isGui()) {
|
||||
LOG("APP: GUI mode\n");
|
||||
|
||||
// Setup the GUI cursor and redraw screen
|
||||
// Load main language (which might be in an extension)
|
||||
Strings::instance()->loadCurrentLanguage();
|
||||
|
||||
// Setup the GUI cursor and redraw screen
|
||||
ui::set_use_native_cursors(preferences().cursor.useNativeCursor());
|
||||
ui::set_mouse_cursor_scale(preferences().cursor.cursorScale());
|
||||
ui::set_mouse_cursor(kArrowCursor);
|
||||
|
@ -41,6 +41,7 @@
|
||||
|
||||
namespace app {
|
||||
|
||||
static const char* kSectionGeneralId = "section_general";
|
||||
static const char* kSectionBgId = "section_bg";
|
||||
static const char* kSectionGridId = "section_grid";
|
||||
static const char* kSectionThemeId = "section_theme";
|
||||
@ -375,6 +376,11 @@ public:
|
||||
onChangeGridScope();
|
||||
sectionListbox()->selectIndex(m_curSection);
|
||||
|
||||
// Refill languages combobox when extensions are enabled/disabled
|
||||
m_extLanguagesChanges =
|
||||
App::instance()->extensions().LanguagesChange.connect(
|
||||
base::Bind<void>(&OptionsWindow::refillLanguages, this));
|
||||
|
||||
// Reload themes when extensions are enabled/disabled
|
||||
m_extThemesChanges =
|
||||
App::instance()->extensions().ThemesChange.connect(
|
||||
@ -386,6 +392,10 @@ public:
|
||||
}
|
||||
|
||||
void saveConfig() {
|
||||
// Update language
|
||||
Strings::instance()->setCurrentLanguage(
|
||||
language()->getItemText(language()->getSelectedItemIndex()));
|
||||
|
||||
m_pref.general.autoshowTimeline(autotimeline()->isSelected());
|
||||
m_pref.general.rewindOnStop(rewindOnStop()->isSelected());
|
||||
m_globPref.timeline.firstFrame(firstFrame()->textInt());
|
||||
@ -603,8 +613,13 @@ private:
|
||||
panel()->showChild(findChild(item->getValue().c_str()));
|
||||
m_curSection = sectionListbox()->getSelectedIndex();
|
||||
|
||||
if (item->getValue() == kSectionBgId)
|
||||
// General section
|
||||
if (item->getValue() == kSectionGeneralId)
|
||||
loadLanguages();
|
||||
// Background section
|
||||
else if (item->getValue() == kSectionBgId)
|
||||
onChangeBgScope();
|
||||
// Grid section
|
||||
else if (item->getValue() == kSectionGridId)
|
||||
onChangeGridScope();
|
||||
// Load themes
|
||||
@ -744,6 +759,25 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void refillLanguages() {
|
||||
language()->removeAllItems();
|
||||
loadLanguages();
|
||||
}
|
||||
|
||||
void loadLanguages() {
|
||||
// Languages already loaded
|
||||
if (language()->getItemCount() > 0)
|
||||
return;
|
||||
|
||||
Strings* strings = Strings::instance();
|
||||
std::string curLang = strings->currentLanguage();
|
||||
for (const std::string& lang : strings->availableLanguages()) {
|
||||
int i = language()->addItem(lang);
|
||||
if (lang == curLang)
|
||||
language()->setSelectedItemIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
void reloadThemes() {
|
||||
while (themeList()->firstChild())
|
||||
delete themeList()->lastChild();
|
||||
@ -1091,6 +1125,7 @@ private:
|
||||
DocumentPreferences& m_docPref;
|
||||
DocumentPreferences* m_curPref;
|
||||
int& m_curSection;
|
||||
obs::scoped_connection m_extLanguagesChanges;
|
||||
obs::scoped_connection m_extThemesChanges;
|
||||
std::string m_restoreThisTheme;
|
||||
int m_restoreScreenScaling;
|
||||
|
@ -220,6 +220,11 @@ Extension::~Extension()
|
||||
it.second.destroyMatrix();
|
||||
}
|
||||
|
||||
void Extension::addLanguage(const std::string& id, const std::string& path)
|
||||
{
|
||||
m_languages[id] = path;
|
||||
}
|
||||
|
||||
void Extension::addTheme(const std::string& id, const std::string& path)
|
||||
{
|
||||
m_themes[id] = path;
|
||||
@ -419,6 +424,19 @@ Extensions::~Extensions()
|
||||
delete ext;
|
||||
}
|
||||
|
||||
std::string Extensions::languagePath(const std::string& langId)
|
||||
{
|
||||
for (auto ext : m_extensions) {
|
||||
if (!ext->isEnabled()) // Ignore disabled extensions
|
||||
continue;
|
||||
|
||||
auto it = ext->languages().find(langId);
|
||||
if (it != ext->languages().end())
|
||||
return it->second;
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string Extensions::themePath(const std::string& themeId)
|
||||
{
|
||||
for (auto ext : m_extensions) {
|
||||
@ -630,6 +648,24 @@ Extension* Extensions::loadExtension(const std::string& path,
|
||||
|
||||
auto contributes = json["contributes"];
|
||||
if (contributes.is_object()) {
|
||||
// Languages
|
||||
auto languages = contributes["languages"];
|
||||
if (languages.is_array()) {
|
||||
for (const auto& lang : languages.array_items()) {
|
||||
std::string langId = lang["id"].string_value();
|
||||
std::string langPath = lang["path"].string_value();
|
||||
|
||||
// The path must be always relative to the extension
|
||||
langPath = base::join_path(path, langPath);
|
||||
|
||||
LOG("EXT: New language '%s' in '%s'\n",
|
||||
langId.c_str(),
|
||||
langPath.c_str());
|
||||
|
||||
extension->addLanguage(langId, langPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Themes
|
||||
auto themes = contributes["themes"];
|
||||
if (themes.is_array()) {
|
||||
@ -695,6 +731,7 @@ Extension* Extensions::loadExtension(const std::string& path,
|
||||
|
||||
void Extensions::generateExtensionSignals(Extension* extension)
|
||||
{
|
||||
if (extension->hasLanguages()) LanguagesChange(extension);
|
||||
if (extension->hasThemes()) ThemesChange(extension);
|
||||
if (extension->hasPalettes()) PalettesChange(extension);
|
||||
if (extension->hasDitheringMatrices()) DitheringMatricesChange(extension);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2017 David Capello
|
||||
// Copyright (C) 2017-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -67,9 +67,11 @@ namespace app {
|
||||
const std::string& version() const { return m_version; }
|
||||
const std::string& displayName() const { return m_displayName; }
|
||||
|
||||
const ExtensionItems& languages() const { return m_languages; }
|
||||
const ExtensionItems& themes() const { return m_themes; }
|
||||
const ExtensionItems& palettes() const { return m_palettes; }
|
||||
|
||||
void addLanguage(const std::string& id, const std::string& path);
|
||||
void addTheme(const std::string& id, const std::string& path);
|
||||
void addPalette(const std::string& id, const std::string& path);
|
||||
void addDitheringMatrix(const std::string& id,
|
||||
@ -81,6 +83,7 @@ namespace app {
|
||||
bool canBeDisabled() const;
|
||||
bool canBeUninstalled() const;
|
||||
|
||||
bool hasLanguages() const { return !m_languages.empty(); }
|
||||
bool hasThemes() const { return !m_themes.empty(); }
|
||||
bool hasPalettes() const { return !m_palettes.empty(); }
|
||||
bool hasDitheringMatrices() const { return !m_ditheringMatrices.empty(); }
|
||||
@ -92,6 +95,7 @@ namespace app {
|
||||
bool isCurrentTheme() const;
|
||||
bool isDefaultTheme() const;
|
||||
|
||||
ExtensionItems m_languages;
|
||||
ExtensionItems m_themes;
|
||||
ExtensionItems m_palettes;
|
||||
std::map<std::string, DitheringMatrixInfo> m_ditheringMatrices;
|
||||
@ -121,6 +125,7 @@ namespace app {
|
||||
Extension* installCompressedExtension(const std::string& zipFn,
|
||||
const ExtensionInfo& info);
|
||||
|
||||
std::string languagePath(const std::string& langId);
|
||||
std::string themePath(const std::string& themeId);
|
||||
std::string palettePath(const std::string& palId);
|
||||
ExtensionItems palettes() const;
|
||||
@ -128,6 +133,7 @@ namespace app {
|
||||
std::vector<Extension::DitheringMatrixInfo> ditheringMatrices();
|
||||
|
||||
obs::signal<void(Extension*)> NewExtension;
|
||||
obs::signal<void(Extension*)> LanguagesChange;
|
||||
obs::signal<void(Extension*)> ThemesChange;
|
||||
obs::signal<void(Extension*)> PalettesChange;
|
||||
obs::signal<void(Extension*)> DitheringMatricesChange;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016, 2017 David Capello
|
||||
// Copyright (C) 2016-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -10,8 +10,11 @@
|
||||
|
||||
#include "app/i18n/strings.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/extensions.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/resource_finder.h"
|
||||
#include "app/ui/main_window.h"
|
||||
#include "app/xml_document.h"
|
||||
#include "app/xml_exception.h"
|
||||
#include "base/fs.h"
|
||||
@ -19,6 +22,8 @@
|
||||
|
||||
namespace app {
|
||||
|
||||
static const char* kDefLanguage = "en";
|
||||
|
||||
// static
|
||||
Strings* Strings::instance()
|
||||
{
|
||||
@ -30,16 +35,101 @@ Strings* Strings::instance()
|
||||
|
||||
Strings::Strings()
|
||||
{
|
||||
const std::string lang = Preferences::instance().general.language();
|
||||
LOG("I18N: Loading strings/%s.ini file\n", lang.c_str());
|
||||
loadLanguage(kDefLanguage);
|
||||
}
|
||||
|
||||
std::set<std::string> Strings::availableLanguages() const
|
||||
{
|
||||
std::set<std::string> result;
|
||||
|
||||
// Add languages in data/strings/
|
||||
ResourceFinder rf;
|
||||
rf.includeDataDir(("strings/" + lang + ".ini").c_str());
|
||||
if (!rf.findFirst())
|
||||
throw base::Exception("strings/" + lang + ".txt was not found");
|
||||
rf.includeDataDir("strings");
|
||||
while (rf.next()) {
|
||||
if (!base::is_directory(rf.filename()))
|
||||
continue;
|
||||
|
||||
for (const auto& fn : base::list_files(rf.filename())) {
|
||||
const std::string langId = base::get_file_title(fn);
|
||||
result.insert(langId);
|
||||
}
|
||||
}
|
||||
|
||||
// Add languages in extensions
|
||||
for (const auto& ext : App::instance()->extensions()) {
|
||||
if (ext->isEnabled() &&
|
||||
ext->hasLanguages()) {
|
||||
for (const auto& langId : ext->languages())
|
||||
result.insert(langId.first);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(result.find(kDefLanguage) != result.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Strings::currentLanguage() const
|
||||
{
|
||||
return Preferences::instance().general.language();
|
||||
}
|
||||
|
||||
void Strings::setCurrentLanguage(const std::string& langId)
|
||||
{
|
||||
// Do nothing (same language)
|
||||
if (currentLanguage() == langId)
|
||||
return;
|
||||
|
||||
Preferences::instance().general.language(langId);
|
||||
loadLanguage(langId);
|
||||
|
||||
// Reload menus
|
||||
App::instance()->mainWindow()->reloadMenus();
|
||||
}
|
||||
|
||||
// Called when extensions are available
|
||||
void Strings::loadCurrentLanguage()
|
||||
{
|
||||
std::string langId = currentLanguage();
|
||||
if (langId != kDefLanguage)
|
||||
loadLanguage(langId);
|
||||
}
|
||||
|
||||
void Strings::loadLanguage(const std::string& langId)
|
||||
{
|
||||
m_strings.clear();
|
||||
loadStringsFromDataDir(kDefLanguage);
|
||||
if (langId != kDefLanguage) {
|
||||
loadStringsFromDataDir(langId);
|
||||
loadStringsFromExtension(langId);
|
||||
}
|
||||
}
|
||||
|
||||
void Strings::loadStringsFromDataDir(const std::string& langId)
|
||||
{
|
||||
// Load the English language file from the Aseprite data directory (so we have the most update list of strings)
|
||||
LOG("I18N: Loading strings/%s.ini file\n", langId.c_str());
|
||||
ResourceFinder rf;
|
||||
rf.includeDataDir(("strings/" + langId + ".ini").c_str());
|
||||
if (!rf.findFirst()) {
|
||||
LOG("strings/%s.ini was not found", langId.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
loadStringsFromFile(rf.filename());
|
||||
}
|
||||
|
||||
void Strings::loadStringsFromExtension(const std::string& langId)
|
||||
{
|
||||
Extensions& exts = App::instance()->extensions();
|
||||
std::string fn = exts.languagePath(langId);
|
||||
if (!fn.empty() && base::is_file(fn))
|
||||
loadStringsFromFile(fn);
|
||||
}
|
||||
|
||||
void Strings::loadStringsFromFile(const std::string& fn)
|
||||
{
|
||||
cfg::CfgFile cfg;
|
||||
cfg.load(rf.filename());
|
||||
cfg.load(fn);
|
||||
|
||||
std::vector<std::string> sections;
|
||||
std::vector<std::string> keys;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016-2017 David Capello
|
||||
// Copyright (C) 2016-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -8,9 +8,9 @@
|
||||
#define APP_I18N_STRINGS_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "strings.ini.h"
|
||||
|
||||
@ -23,9 +23,19 @@ namespace app {
|
||||
|
||||
const std::string& translate(const char* id) const;
|
||||
|
||||
void loadCurrentLanguage();
|
||||
std::set<std::string> availableLanguages() const;
|
||||
std::string currentLanguage() const;
|
||||
void setCurrentLanguage(const std::string& langId);
|
||||
|
||||
private:
|
||||
Strings();
|
||||
|
||||
void loadLanguage(const std::string& langId);
|
||||
void loadStringsFromDataDir(const std::string& langId);
|
||||
void loadStringsFromExtension(const std::string& langId);
|
||||
void loadStringsFromFile(const std::string& fn);
|
||||
|
||||
mutable std::unordered_map<std::string, std::string> m_strings;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user