mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-13 15:39:59 +00:00
With this we can finally easily save font presets, even references to external .ttf files.
This commit is contained in:
parent
77dd92c679
commit
6093282ac3
@ -777,6 +777,7 @@ preview = &Preview
|
||||
tiled = &Tiled
|
||||
|
||||
[font_popup]
|
||||
pinned_fonts = Pinned Fonts
|
||||
theme_fonts = Theme Fonts
|
||||
system_fonts = System Fonts
|
||||
load = Load External Font
|
||||
|
@ -111,6 +111,34 @@ void FontInfo::updatePreferences()
|
||||
}
|
||||
}
|
||||
|
||||
std::string FontInfo::humanString() const
|
||||
{
|
||||
std::string result;
|
||||
|
||||
switch (type()) {
|
||||
case app::FontInfo::Type::Unknown:
|
||||
case app::FontInfo::Type::Name:
|
||||
case app::FontInfo::Type::System:
|
||||
result = name();
|
||||
break;
|
||||
case app::FontInfo::Type::File:
|
||||
result = base::get_file_name(name());
|
||||
break;
|
||||
}
|
||||
result += fmt::format(" {}pt", size());
|
||||
if (!result.empty()) {
|
||||
if (style().weight() >= text::FontStyle::Weight::SemiBold)
|
||||
result += " Bold";
|
||||
if (style().slant() != text::FontStyle::Slant::Upright)
|
||||
result += " Italic";
|
||||
if (antialias())
|
||||
result += " Antialias";
|
||||
if (ligatures())
|
||||
result += " Ligatures";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
||||
namespace base {
|
||||
|
@ -73,6 +73,8 @@ namespace app {
|
||||
static FontInfo getFromPreferences();
|
||||
void updatePreferences();
|
||||
|
||||
std::string humanString() const;
|
||||
|
||||
bool operator==(const FontInfo& other) const {
|
||||
return (m_type == other.m_type &&
|
||||
m_name == other.m_name &&
|
||||
|
@ -21,13 +21,11 @@
|
||||
|
||||
namespace {
|
||||
|
||||
enum { kPinnedFiles, kRecentFiles,
|
||||
kPinnedPaths, kRecentPaths };
|
||||
|
||||
const char* kSectionName[] = { "PinnedFiles",
|
||||
"RecentFiles",
|
||||
"PinnedPaths",
|
||||
"RecentPaths" };
|
||||
"RecentPaths",
|
||||
"PinnedFonts" };
|
||||
|
||||
// Special key used in recent sections (files/paths) to indicate that
|
||||
// the section was already converted at least one time.
|
||||
@ -183,7 +181,7 @@ void RecentFiles::load()
|
||||
!get_config_bool(section, kConversionKey, false));
|
||||
|
||||
const bool processOldPaths =
|
||||
(i == kRecentPaths &&
|
||||
(i == kRecentFolders &&
|
||||
get_config_string(section, "Path00", nullptr) &&
|
||||
!get_config_bool(section, kConversionKey, false));
|
||||
|
||||
@ -197,11 +195,14 @@ void RecentFiles::load()
|
||||
|
||||
const char* fn = get_config_string(section, key.c_str(), nullptr);
|
||||
if (fn && *fn &&
|
||||
((i < 2 && base::is_file(fn)) ||
|
||||
(i >= 2 && base::is_directory(fn)))) {
|
||||
(((i == kPinnedFiles || i == kRecentFiles) && base::is_file(fn)) ||
|
||||
((i == kPinnedFolders || i == kRecentFolders) && base::is_directory(fn)))) {
|
||||
std::string normalFn = normalizePath(fn);
|
||||
m_paths[i].push_back(normalFn);
|
||||
}
|
||||
else if (i == kPinnedFonts) {
|
||||
m_paths[i].push_back(fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -215,7 +216,7 @@ void RecentFiles::save()
|
||||
if ((i == kRecentFiles &&
|
||||
(std::strncmp(key.c_str(), "Filename", 8) == 0 || key == kConversionKey))
|
||||
||
|
||||
(i == kRecentPaths &&
|
||||
(i == kRecentFolders &&
|
||||
(std::strncmp(key.c_str(), "Path", 4) == 0 || key == kConversionKey))) {
|
||||
// Ignore old entries if we are going to read the new ones
|
||||
continue;
|
||||
@ -229,7 +230,7 @@ void RecentFiles::save()
|
||||
m_paths[i][j].c_str());
|
||||
}
|
||||
// Special entry that indicates that we've already converted
|
||||
if ((i == kRecentFiles || i == kRecentPaths) &&
|
||||
if ((i == kRecentFiles || i == kRecentFolders) &&
|
||||
!get_config_bool(section, kConversionKey, false)) {
|
||||
set_config_bool(section, kConversionKey, true);
|
||||
}
|
||||
|
@ -17,16 +17,22 @@
|
||||
namespace app {
|
||||
|
||||
class RecentFiles {
|
||||
enum { kPinnedFiles,
|
||||
enum {
|
||||
kPinnedFiles,
|
||||
kRecentFiles,
|
||||
kPinnedFolders,
|
||||
kRecentFolders,
|
||||
kCollections };
|
||||
kPinnedFonts,
|
||||
kCollections
|
||||
};
|
||||
|
||||
public:
|
||||
const base::paths& pinnedFiles() const { return m_paths[kPinnedFiles]; }
|
||||
const base::paths& recentFiles() const { return m_paths[kRecentFiles]; }
|
||||
const base::paths& pinnedFolders() const { return m_paths[kPinnedFolders]; }
|
||||
const base::paths& recentFolders() const { return m_paths[kRecentFolders]; }
|
||||
// TODO probably this collection should be in another kind of class.
|
||||
base::paths& pinnedFonts() { return m_paths[kPinnedFonts]; }
|
||||
|
||||
RecentFiles(const int limit);
|
||||
~RecentFiles();
|
||||
|
@ -10,8 +10,11 @@
|
||||
|
||||
#include "app/ui/font_entry.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/console.h"
|
||||
#include "app/recent_files.h"
|
||||
#include "app/ui/font_popup.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "base/scoped_value.h"
|
||||
#include "fmt/format.h"
|
||||
#include "ui/display.h"
|
||||
@ -19,6 +22,7 @@
|
||||
#include "ui/message.h"
|
||||
#include "ui/scale.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace app {
|
||||
@ -84,6 +88,9 @@ bool FontEntry::FontFace::onProcessMessage(Message* msg)
|
||||
}
|
||||
}
|
||||
if (!m_popup->isVisible()) {
|
||||
// Reset the search filter before opening the popup window.
|
||||
m_popup->setSearchText(std::string());
|
||||
|
||||
m_popup->showPopup(display(), bounds());
|
||||
requestFocus();
|
||||
}
|
||||
@ -171,6 +178,47 @@ void FontEntry::FontFace::onChange()
|
||||
FontChange(m_popup->selectedFont(), From::Face);
|
||||
}
|
||||
|
||||
os::Surface* FontEntry::FontFace::onGetCloseIcon() const
|
||||
{
|
||||
auto& pinnedFonts = App::instance()->recentFiles()->pinnedFonts();
|
||||
const FontInfo info = fontEntry()->info();
|
||||
const std::string fontInfoStr = base::convert_to<std::string>(info);
|
||||
auto it = std::find(pinnedFonts.begin(),
|
||||
pinnedFonts.end(),
|
||||
fontInfoStr);
|
||||
if (it != pinnedFonts.end()) {
|
||||
return skin::SkinTheme::get(this)->parts.pinned()->bitmap(0);
|
||||
}
|
||||
return skin::SkinTheme::get(this)->parts.unpinned()->bitmap(0);
|
||||
}
|
||||
|
||||
void FontEntry::FontFace::onCloseIconPressed()
|
||||
{
|
||||
const FontInfo info = fontEntry()->info();
|
||||
if (info.size() == 0) // Don't save fonts with size=0pt
|
||||
return;
|
||||
|
||||
auto& pinnedFonts = App::instance()->recentFiles()->pinnedFonts();
|
||||
const std::string fontInfoStr = base::convert_to<std::string>(info);
|
||||
|
||||
auto it = std::find(pinnedFonts.begin(),
|
||||
pinnedFonts.end(),
|
||||
fontInfoStr);
|
||||
if (it != pinnedFonts.end()) {
|
||||
pinnedFonts.erase(it);
|
||||
}
|
||||
else {
|
||||
pinnedFonts.push_back(fontInfoStr);
|
||||
std::sort(pinnedFonts.begin(),
|
||||
pinnedFonts.end());
|
||||
}
|
||||
|
||||
// Refill the list with the new pinned/unpinned item
|
||||
m_popup->recreatePinnedItems();
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
FontEntry::FontSize::FontSize()
|
||||
{
|
||||
setEditable(true);
|
||||
@ -215,13 +263,16 @@ FontEntry::FontEntry()
|
||||
|
||||
m_face.setMinSize(gfx::Size(128*guiscale(), 0));
|
||||
|
||||
m_face.FontChange.connect([this](const FontInfo& newTypeName,
|
||||
const From from) {
|
||||
m_face.FontChange.connect([this](const FontInfo& newTypeName, const From from) {
|
||||
if (newTypeName.size() > 0)
|
||||
setInfo(newTypeName, from);
|
||||
else {
|
||||
setInfo(FontInfo(newTypeName,
|
||||
m_info.size(),
|
||||
m_info.style(),
|
||||
m_info.flags()),
|
||||
from);
|
||||
}
|
||||
invalidate();
|
||||
});
|
||||
|
||||
|
@ -48,6 +48,8 @@ namespace app {
|
||||
protected:
|
||||
bool onProcessMessage(ui::Message* msg) override;
|
||||
void onChange() override;
|
||||
os::Surface* onGetCloseIcon() const override;
|
||||
void onCloseIconPressed() override;
|
||||
private:
|
||||
FontEntry* fontEntry() const { return static_cast<FontEntry*>(parent()); }
|
||||
|
||||
|
@ -11,11 +11,13 @@
|
||||
|
||||
#include "app/ui/font_popup.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/file_selector.h"
|
||||
#include "app/font_info.h"
|
||||
#include "app/font_path.h"
|
||||
#include "app/i18n/strings.h"
|
||||
#include "app/match_words.h"
|
||||
#include "app/recent_files.h"
|
||||
#include "app/ui/separator_in_view.h"
|
||||
#include "app/ui/skin/font_data.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
@ -60,6 +62,12 @@ class FontItem : public ListItem {
|
||||
public:
|
||||
struct ByName { };
|
||||
|
||||
explicit FontItem(const FontInfo& fontInfo)
|
||||
: ListItem(fontInfo.humanString())
|
||||
, m_fontInfo(fontInfo) {
|
||||
getCachedThumbnail();
|
||||
}
|
||||
|
||||
FontItem(const std::string& name, ByName)
|
||||
: ListItem(name)
|
||||
, m_fontInfo(FontInfo::Type::Name, name,
|
||||
@ -69,7 +77,7 @@ public:
|
||||
getCachedThumbnail();
|
||||
}
|
||||
|
||||
FontItem(const std::string& fn)
|
||||
explicit FontItem(const std::string& fn)
|
||||
: ListItem(base::get_file_title(fn))
|
||||
, m_fontInfo(FontInfo::Type::File, fn,
|
||||
FontInfo::kDefaultSize,
|
||||
@ -128,9 +136,14 @@ private:
|
||||
const auto* theme = app::skin::SkinTheme::get(this);
|
||||
|
||||
try {
|
||||
const FontInfo fontInfoDefSize(m_fontInfo,
|
||||
FontInfo::kDefaultSize,
|
||||
text::FontStyle(),
|
||||
FontInfo::Flags::Antialias);
|
||||
|
||||
const gfx::Color color = theme->colors.text();
|
||||
doc::ImageRef image =
|
||||
render_text(m_fontInfo, text(), color);
|
||||
render_text(fontInfoDefSize, text(), color);
|
||||
if (!image)
|
||||
return;
|
||||
|
||||
@ -220,13 +233,17 @@ FontPopup::FontPopup(const FontInfo& fontInfo)
|
||||
|
||||
m_popup->view()->attachToView(&m_listBox);
|
||||
|
||||
// Pinned fonts
|
||||
m_pinnedSeparator = new SeparatorInView(Strings::font_popup_pinned_fonts());
|
||||
m_listBox.addChild(m_pinnedSeparator);
|
||||
|
||||
// Default fonts
|
||||
bool firstThemeFont = true;
|
||||
bool first = true;
|
||||
for (auto kv : skin::SkinTheme::get(this)->getWellKnownFonts()) {
|
||||
if (!kv.second->filename().empty()) {
|
||||
if (firstThemeFont) {
|
||||
if (first) {
|
||||
m_listBox.addChild(new SeparatorInView(Strings::font_popup_theme_fonts()));
|
||||
firstThemeFont = false;
|
||||
first = false;
|
||||
}
|
||||
m_listBox.addChild(new FontItem(kv.first, FontItem::ByName()));
|
||||
}
|
||||
@ -332,6 +349,8 @@ void FontPopup::showPopup(Display* display,
|
||||
{
|
||||
m_listBox.selectChild(nullptr);
|
||||
|
||||
recreatePinnedItems();
|
||||
|
||||
ui::fit_bounds(display, this,
|
||||
gfx::Rect(buttonBounds.x, buttonBounds.y2(),
|
||||
buttonBounds.w*2, buttonBounds.h),
|
||||
@ -344,6 +363,28 @@ void FontPopup::showPopup(Display* display,
|
||||
openWindow();
|
||||
}
|
||||
|
||||
void FontPopup::recreatePinnedItems()
|
||||
{
|
||||
// Update list of pinned fonts
|
||||
if (m_pinnedSeparator) {
|
||||
// Delete pinned elements
|
||||
while (true) {
|
||||
Widget* next = m_pinnedSeparator->nextSibling();
|
||||
if (!next || next->type() == kSeparatorWidget)
|
||||
break;
|
||||
delete next;
|
||||
}
|
||||
|
||||
// Recreate pinned elements
|
||||
auto& pinnedFonts = App::instance()->recentFiles()->pinnedFonts();
|
||||
int i = 1;
|
||||
for (const auto& fontInfoStr : pinnedFonts) {
|
||||
m_listBox.insertChild(i++, new FontItem(base::convert_to<FontInfo>(fontInfoStr)));
|
||||
}
|
||||
m_pinnedSeparator->setVisible(!pinnedFonts.empty());
|
||||
}
|
||||
}
|
||||
|
||||
FontInfo FontPopup::selectedFont()
|
||||
{
|
||||
const FontItem* child = dynamic_cast<FontItem*>(m_listBox.getSelectedChild());
|
||||
|
@ -42,6 +42,8 @@ namespace app {
|
||||
void showPopup(ui::Display* display,
|
||||
const gfx::Rect& buttonBounds);
|
||||
|
||||
void recreatePinnedItems();
|
||||
|
||||
FontListBox* getListBox() { return &m_listBox; }
|
||||
FontInfo selectedFont();
|
||||
|
||||
@ -59,6 +61,7 @@ namespace app {
|
||||
gen::FontPopup* m_popup;
|
||||
FontListBox m_listBox;
|
||||
ui::Timer m_timer;
|
||||
ui::Widget* m_pinnedSeparator = nullptr;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -40,8 +40,7 @@ bool SearchEntry::onProcessMessage(ui::Message* msg)
|
||||
- bounds().origin();
|
||||
|
||||
if (closeBounds.contains(mousePos)) {
|
||||
setText("");
|
||||
onChange();
|
||||
onCloseIconPressed();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
@ -63,7 +62,7 @@ void SearchEntry::onPaint(ui::PaintEvent& ev)
|
||||
bounds.y + bounds.h/2 - icon->height()/2);
|
||||
|
||||
if (!text().empty()) {
|
||||
icon = theme->parts.iconClose()->bitmap(0);
|
||||
icon = onGetCloseIcon();
|
||||
ev.graphics()->drawColoredRgbaSurface(
|
||||
icon, theme->colors.text(),
|
||||
bounds.x + bounds.w - border().right() - childSpacing() - icon->width(),
|
||||
@ -88,17 +87,27 @@ Rect SearchEntry::onGetEntryTextBounds() const
|
||||
auto theme = SkinTheme::get(this);
|
||||
Rect bounds = Entry::onGetEntryTextBounds();
|
||||
auto icon1 = theme->parts.iconSearch()->bitmap(0);
|
||||
auto icon2 = theme->parts.iconClose()->bitmap(0);
|
||||
auto icon2 = onGetCloseIcon();
|
||||
bounds.x += childSpacing() + icon1->width();
|
||||
bounds.w -= 2*childSpacing() + icon1->width() + icon2->width();
|
||||
return bounds;
|
||||
}
|
||||
|
||||
os::Surface* SearchEntry::onGetCloseIcon() const
|
||||
{
|
||||
return SkinTheme::get(this)->parts.iconClose()->bitmap(0);
|
||||
}
|
||||
|
||||
void SearchEntry::onCloseIconPressed()
|
||||
{
|
||||
setText("");
|
||||
onChange();
|
||||
}
|
||||
|
||||
Rect SearchEntry::getCloseIconBounds() const
|
||||
{
|
||||
auto theme = SkinTheme::get(this);
|
||||
Rect bounds = clientBounds();
|
||||
auto icon = theme->parts.iconClose()->bitmap(0);
|
||||
auto icon = onGetCloseIcon();
|
||||
bounds.x += bounds.w - border().right() - childSpacing() - icon->width();
|
||||
bounds.y += bounds.h/2 - icon->height()/2;
|
||||
bounds.w = icon->width();
|
||||
|
@ -23,6 +23,9 @@ namespace app {
|
||||
void onSizeHint(ui::SizeHintEvent& ev) override;
|
||||
gfx::Rect onGetEntryTextBounds() const override;
|
||||
|
||||
virtual os::Surface* onGetCloseIcon() const;
|
||||
virtual void onCloseIconPressed();
|
||||
|
||||
private:
|
||||
gfx::Rect getCloseIconBounds() const;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user