mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-16 23:42:57 +00:00
Add new pref="" attribute to <check> widgets to bind check boxes with bool preference options automatically
With this change we've moved the propagateToChildren/propagateToParent flags from ui::KeyMessage to ui::Message so anykind of message (e.g. user defined messages like kSavePreferencesMessage) can use these flags (processed by ui::Widget::onProcessMessage()).
This commit is contained in:
parent
05f6aec8b2
commit
7a35eb26a1
@ -145,8 +145,10 @@
|
||||
<!-- Timeline -->
|
||||
<vbox id="section_timeline">
|
||||
<separator text="@.section_timeline" horizontal="true" />
|
||||
<check text="@.autotimeline" id="autotimeline" tooltip="@.autotimeline_tooltip" />
|
||||
<check text="@.rewind_on_stop" id="rewind_on_stop" tooltip="@.rewind_on_stop_tooltip" />
|
||||
<check text="@.autotimeline" id="autotimeline" tooltip="@.autotimeline_tooltip"
|
||||
pref="general.autoshow_timeline" />
|
||||
<check text="@.rewind_on_stop" id="rewind_on_stop" tooltip="@.rewind_on_stop_tooltip"
|
||||
pref="general.rewind_on_stop" />
|
||||
<hbox>
|
||||
<label text="@.default_first_frame" />
|
||||
<entry id="first_frame" maxsize="3" />
|
||||
|
@ -366,6 +366,7 @@ if(ENABLE_UI)
|
||||
ui/palette_view.cpp
|
||||
ui/palettes_listbox.cpp
|
||||
ui/popup_window_pin.cpp
|
||||
ui/pref_widget.cpp
|
||||
ui/preview_editor.cpp
|
||||
ui/recent_listbox.cpp
|
||||
ui/resources_listbox.cpp
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "app/recent_files.h"
|
||||
#include "app/resource_finder.h"
|
||||
#include "app/ui/color_button.h"
|
||||
#include "app/ui/pref_widget.h"
|
||||
#include "app/ui/separator_in_view.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "base/bind.h"
|
||||
@ -188,12 +189,6 @@ public:
|
||||
defaultSliceColor()->setColor(m_pref.slices.defaultColor());
|
||||
|
||||
// Others
|
||||
if (m_pref.general.autoshowTimeline())
|
||||
autotimeline()->setSelected(true);
|
||||
|
||||
if (m_pref.general.rewindOnStop())
|
||||
rewindOnStop()->setSelected(true);
|
||||
|
||||
firstFrame()->setTextf("%d", m_globPref.timeline.firstFrame());
|
||||
|
||||
if (m_pref.general.expandMenubarOnMouseover())
|
||||
@ -397,12 +392,17 @@ public:
|
||||
}
|
||||
|
||||
void saveConfig() {
|
||||
// Save preferences in widgets that are bound to options automatically
|
||||
{
|
||||
Message* msg = new Message(kSavePreferencesMessage);
|
||||
msg->setPropagateToChildren(msg);
|
||||
sendMessage(msg);
|
||||
}
|
||||
|
||||
// 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());
|
||||
m_pref.general.showFullPath(showFullPath()->isSelected());
|
||||
m_pref.saveFile.defaultExtension(getExtension(defaultExtension()));
|
||||
|
@ -13,11 +13,17 @@
|
||||
|
||||
namespace app {
|
||||
|
||||
class OptionBase;
|
||||
|
||||
class Section {
|
||||
public:
|
||||
Section(const std::string& name) : m_name(name) { }
|
||||
virtual ~Section() { }
|
||||
const char* name() const { return m_name.c_str(); }
|
||||
|
||||
virtual Section* section(const char* id) = 0;
|
||||
virtual OptionBase* option(const char* id) = 0;
|
||||
|
||||
obs::signal<void()> BeforeChange;
|
||||
obs::signal<void()> AfterChange;
|
||||
|
||||
@ -31,6 +37,7 @@ namespace app {
|
||||
: m_section(section)
|
||||
, m_id(id) {
|
||||
}
|
||||
virtual ~OptionBase() { }
|
||||
const char* section() const { return m_section->name(); }
|
||||
const char* id() const { return m_id; }
|
||||
protected:
|
||||
|
17
src/app/ui/pref_widget.cpp
Normal file
17
src/app/ui/pref_widget.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018 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/ui/pref_widget.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
ui::RegisterMessage kSavePreferencesMessage;
|
||||
|
||||
} // namespace app
|
70
src/app/ui/pref_widget.h
Normal file
70
src/app/ui/pref_widget.h
Normal file
@ -0,0 +1,70 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_UI_PREF_WIDGET_H_INCLUDED
|
||||
#define APP_UI_PREF_WIDGET_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/pref/preferences.h"
|
||||
#include "base/split_string.h"
|
||||
#include "base/exception.h"
|
||||
#include "ui/message.h"
|
||||
#include "ui/register_message.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
extern ui::RegisterMessage kSavePreferencesMessage;
|
||||
|
||||
template<class Base>
|
||||
class BoolPrefWidget : public Base {
|
||||
public:
|
||||
template<typename...Args>
|
||||
BoolPrefWidget(Args&&...args)
|
||||
: Base(args...)
|
||||
, m_option(nullptr) {
|
||||
}
|
||||
|
||||
void setPref(const char* prefString) {
|
||||
ASSERT(prefString);
|
||||
|
||||
std::vector<std::string> parts;
|
||||
base::split_string(prefString, parts, ".");
|
||||
if (parts.size() == 2) {
|
||||
auto& pref = Preferences::instance();
|
||||
auto section = pref.section(parts[0].c_str());
|
||||
if (!section)
|
||||
throw base::Exception("Preference section not found: %s", prefString);
|
||||
|
||||
m_option =
|
||||
dynamic_cast<Option<bool>*>(
|
||||
section->option(parts[1].c_str()));
|
||||
|
||||
if (!m_option)
|
||||
throw base::Exception("Preference option not found: %s", prefString);
|
||||
|
||||
// Load option value
|
||||
this->setSelected((*m_option)());
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
bool onProcessMessage(ui::Message* msg) override {
|
||||
if (msg->type() == kSavePreferencesMessage) {
|
||||
ASSERT(m_option);
|
||||
|
||||
// Update Option value.
|
||||
(*m_option)(this->isSelected());
|
||||
}
|
||||
return Base::onProcessMessage(msg);
|
||||
}
|
||||
|
||||
private:
|
||||
Option<bool>* m_option;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -8,6 +8,8 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/ui/pref_widget.h"
|
||||
|
||||
#include "app/widget_loader.h"
|
||||
|
||||
#include "app/app.h"
|
||||
@ -184,15 +186,29 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
|
||||
}
|
||||
}
|
||||
else if (elem_name == "check") {
|
||||
const char *looklike = elem->Attribute("looklike");
|
||||
const char* looklike = elem->Attribute("looklike");
|
||||
const char* pref = elem->Attribute("pref");
|
||||
|
||||
ASSERT(!widget || !pref); // widget && pref is not supported
|
||||
|
||||
if (looklike != NULL && strcmp(looklike, "button") == 0) {
|
||||
ASSERT(!pref); // not supported yet
|
||||
|
||||
if (!widget)
|
||||
widget = new CheckBox("", kButtonWidget);
|
||||
}
|
||||
else {
|
||||
if (!widget)
|
||||
widget = new CheckBox("");
|
||||
if (!widget) {
|
||||
// Automatic bind <check> widget with bool preference option
|
||||
if (pref) {
|
||||
auto prefWidget = new BoolPrefWidget<CheckBox>("");
|
||||
prefWidget->setPref(pref);
|
||||
widget = prefWidget;
|
||||
}
|
||||
else {
|
||||
widget = new CheckBox("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool center = bool_attr_is_true(elem, "center");
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Code Generator
|
||||
// Copyright (c) 2014-2016 David Capello
|
||||
// Copyright (c) 2014-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -29,7 +29,9 @@ static void print_pref_class_def(TiXmlElement* elem, const std::string& classNam
|
||||
|
||||
std::cout
|
||||
<< indent << " void load();\n"
|
||||
<< indent << " void save();\n";
|
||||
<< indent << " void save();\n"
|
||||
<< indent << " Section* section(const char* id) override;\n"
|
||||
<< indent << " OptionBase* option(const char* id) override;\n";
|
||||
|
||||
TiXmlElement* child = (elem->FirstChild() ? elem->FirstChild()->ToElement(): NULL);
|
||||
while (child) {
|
||||
@ -100,6 +102,8 @@ static void print_pref_class_impl(TiXmlElement* elem, const std::string& prefix,
|
||||
<< "{\n"
|
||||
<< "}\n";
|
||||
|
||||
// Section::load()
|
||||
|
||||
std::cout
|
||||
<< "\n"
|
||||
<< "void " << prefix << className << "::load()\n"
|
||||
@ -136,7 +140,11 @@ static void print_pref_class_impl(TiXmlElement* elem, const std::string& prefix,
|
||||
|
||||
std::cout
|
||||
<< "}\n"
|
||||
<< "\n"
|
||||
<< "\n";
|
||||
|
||||
// Section::save()
|
||||
|
||||
std::cout
|
||||
<< "void " << prefix << className << "::save()\n"
|
||||
<< "{\n";
|
||||
|
||||
@ -157,8 +165,58 @@ static void print_pref_class_impl(TiXmlElement* elem, const std::string& prefix,
|
||||
}
|
||||
|
||||
std::cout
|
||||
<< "}\n"
|
||||
<< "\n";
|
||||
|
||||
// Section::section(id)
|
||||
|
||||
std::cout
|
||||
<< "Section* " << prefix << className << "::section(const char* id)\n"
|
||||
<< "{\n";
|
||||
|
||||
child = (elem->FirstChild() ? elem->FirstChild()->ToElement(): NULL);
|
||||
while (child) {
|
||||
if (child->Value()) {
|
||||
std::string name = child->Value();
|
||||
const char* childId = child->Attribute("id");
|
||||
if (name == "section") {
|
||||
std::string memberName = convert_xmlid_to_cppid(childId, false);
|
||||
std::cout << " if (std::strcmp(id, " << memberName << ".name()) == 0) return &" << memberName << ";\n";
|
||||
}
|
||||
}
|
||||
child = child->NextSiblingElement();
|
||||
}
|
||||
|
||||
std::cout
|
||||
<< " return nullptr;\n"
|
||||
<< "}\n"
|
||||
<< "\n";
|
||||
|
||||
// Section::option(id)
|
||||
|
||||
std::cout
|
||||
<< "OptionBase* " << prefix << className << "::option(const char* id)\n"
|
||||
<< "{\n";
|
||||
|
||||
child = (elem->FirstChild() ? elem->FirstChild()->ToElement(): NULL);
|
||||
while (child) {
|
||||
if (child->Value()) {
|
||||
std::string name = child->Value();
|
||||
const char* childId = child->Attribute("id");
|
||||
if (name == "option") {
|
||||
std::string memberName = convert_xmlid_to_cppid(childId, false);
|
||||
std::cout << " if (std::strcmp(id, " << memberName << ".id()) == 0) return &" << memberName << ";\n";
|
||||
}
|
||||
}
|
||||
child = child->NextSiblingElement();
|
||||
}
|
||||
|
||||
std::cout
|
||||
<< " return nullptr;\n"
|
||||
<< "}\n";
|
||||
|
||||
// Sub-sections
|
||||
|
||||
child = (elem->FirstChild() ? elem->FirstChild()->ToElement(): NULL);
|
||||
while (child) {
|
||||
if (child->Value()) {
|
||||
@ -253,6 +311,8 @@ void gen_pref_impl(TiXmlDocument* doc, const std::string& inputFn)
|
||||
<< "#include \"app/pref/option_io.h\"\n"
|
||||
<< "#include \"app/pref/preferences.h\"\n"
|
||||
<< "\n"
|
||||
<< "#include <cstring>\n"
|
||||
<< "\n"
|
||||
<< "namespace app {\n"
|
||||
<< "namespace gen {\n";
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -21,8 +21,7 @@ namespace ui {
|
||||
|
||||
Message::Message(MessageType type, KeyModifiers modifiers)
|
||||
: m_type(type)
|
||||
, m_used(false)
|
||||
, m_fromFilter(false)
|
||||
, m_flags(0)
|
||||
{
|
||||
if (modifiers == kKeyUninitializedModifier && she::instance())
|
||||
m_modifiers = she::instance()->keyModifiers();
|
||||
@ -77,9 +76,8 @@ KeyMessage::KeyMessage(MessageType type,
|
||||
, m_unicodeChar(unicodeChar)
|
||||
, m_repeat(repeat)
|
||||
, m_isDead(false)
|
||||
, m_propagate_to_children(false)
|
||||
, m_propagate_to_parent(true)
|
||||
{
|
||||
setPropagateToParent(true);
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
@ -24,6 +24,12 @@ namespace ui {
|
||||
class Widget;
|
||||
|
||||
class Message {
|
||||
enum Flags {
|
||||
Used = 1, // Message already used/processed by one widget
|
||||
FromFilter = 2, // Sent from pre-filter
|
||||
PropagateToChildren = 4,
|
||||
PropagateToParent = 8,
|
||||
};
|
||||
public:
|
||||
typedef WidgetsList::iterator& recipients_iterator;
|
||||
|
||||
@ -34,10 +40,10 @@ namespace ui {
|
||||
MessageType type() const { return m_type; }
|
||||
const WidgetsList& recipients() const { return m_recipients; }
|
||||
bool hasRecipients() const { return !m_recipients.empty(); }
|
||||
bool isUsed() const { return m_used; }
|
||||
bool fromFilter() const { return m_fromFilter; }
|
||||
void setFromFilter(bool state) { m_fromFilter = state; }
|
||||
void markAsUsed() { m_used = true; }
|
||||
bool isUsed() const { return hasFlag(Used); }
|
||||
bool fromFilter() const { return hasFlag(FromFilter); }
|
||||
void setFromFilter(const bool state) { setFlag(FromFilter, state); }
|
||||
void markAsUsed() { setFlag(Used, true); }
|
||||
KeyModifiers modifiers() const { return m_modifiers; }
|
||||
bool shiftPressed() const { return (m_modifiers & kKeyShiftModifier) == kKeyShiftModifier; }
|
||||
bool ctrlPressed() const { return (m_modifiers & kKeyCtrlModifier) == kKeyCtrlModifier; }
|
||||
@ -56,11 +62,23 @@ namespace ui {
|
||||
|
||||
void broadcastToChildren(Widget* widget);
|
||||
|
||||
bool propagateToChildren() const { return hasFlag(PropagateToChildren); }
|
||||
bool propagateToParent() const { return hasFlag(PropagateToParent); }
|
||||
void setPropagateToChildren(const bool state) { setFlag(PropagateToChildren, state); }
|
||||
void setPropagateToParent(const bool state) { setFlag(PropagateToParent, state); }
|
||||
|
||||
private:
|
||||
bool hasFlag(const Flags flag) const {
|
||||
return (m_flags & flag) == flag;
|
||||
}
|
||||
void setFlag(const Flags flag, const bool state) {
|
||||
m_flags = (state ? (m_flags | flag):
|
||||
(m_flags & ~flag));
|
||||
}
|
||||
|
||||
MessageType m_type; // Type of message
|
||||
WidgetsList m_recipients; // List of recipients of the message
|
||||
bool m_used; // Was used
|
||||
bool m_fromFilter; // Sent from pre-filter
|
||||
int m_flags; // Was used
|
||||
KeyModifiers m_modifiers; // Key modifiers pressed when message was created
|
||||
};
|
||||
|
||||
@ -77,18 +95,12 @@ namespace ui {
|
||||
int repeat() const { return m_repeat; }
|
||||
bool isDeadKey() const { return m_isDead; }
|
||||
void setDeadKey(bool state) { m_isDead = state; }
|
||||
bool propagateToChildren() const { return m_propagate_to_children; }
|
||||
bool propagateToParent() const { return m_propagate_to_parent; }
|
||||
void setPropagateToChildren(bool flag) { m_propagate_to_children = flag; }
|
||||
void setPropagateToParent(bool flag) { m_propagate_to_parent = flag; }
|
||||
|
||||
private:
|
||||
KeyScancode m_scancode;
|
||||
int m_unicodeChar;
|
||||
int m_repeat; // repeat=0 means the first time the key is pressed
|
||||
bool m_isDead;
|
||||
bool m_propagate_to_children;
|
||||
bool m_propagate_to_parent;
|
||||
};
|
||||
|
||||
class PaintMessage : public Message {
|
||||
|
@ -1423,23 +1423,6 @@ bool Widget::onProcessMessage(Message* msg)
|
||||
return paintEvent(graphics.get(), false);
|
||||
}
|
||||
|
||||
case kKeyDownMessage:
|
||||
case kKeyUpMessage:
|
||||
if (static_cast<KeyMessage*>(msg)->propagateToChildren()) {
|
||||
// Broadcast the message to the children.
|
||||
for (auto child : m_children)
|
||||
if (child->sendMessage(msg))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Propagate the message to the parent.
|
||||
if (static_cast<KeyMessage*>(msg)->propagateToParent() &&
|
||||
parent()) {
|
||||
return parent()->sendMessage(msg);
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
case kDoubleClickMessage: {
|
||||
// Convert double clicks into mouse down
|
||||
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
||||
@ -1475,6 +1458,17 @@ bool Widget::onProcessMessage(Message* msg)
|
||||
|
||||
}
|
||||
|
||||
// Broadcast the message to the children.
|
||||
if (msg->propagateToChildren()) {
|
||||
for (auto child : m_children)
|
||||
if (child->sendMessage(msg))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Propagate the message to the parent.
|
||||
if (msg->propagateToParent() && parent())
|
||||
return parent()->sendMessage(msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user