Merge branch 'main' into beta

This commit is contained in:
David Capello 2021-10-20 11:53:47 -03:00
commit 76a815cc2c
8 changed files with 131 additions and 49 deletions

View File

@ -325,17 +325,15 @@ int App::initialize(const AppOptions& options)
// Show the main window (this is not modal, the code continues) // Show the main window (this is not modal, the code continues)
m_mainWindow->openWindow(); m_mainWindow->openWindow();
// Redraw the whole screen. // To know the initial manager size we call to
manager->invalidate(); // Manager::updateAllDisplaysWithNewScale(...) so we receive a
// Manager::onNewDisplayConfiguration() (which will update the
// Pump some messages so we receive the first // bounds of the manager for first time). This is required so if
// Manager::onNewDisplayConfiguration() and we known the manager // the OpenFileCommand (called when we're processing the CLI with
// initial size. This is required so if the OpenFileCommand // OpenBatchOfFiles) shows a dialog to open a sequence of files,
// (called when we're processing the CLI with OpenBatchOfFiles) // the dialog is centered correctly to the manager bounds.
// shows a dialog to open a sequence of files, the dialog is const int scale = Preferences::instance().general.screenScale();
// centered correctly to the manager bounds. manager->updateAllDisplaysWithNewScale(scale);
ui::MessageLoop loop(manager);
loop.pumpMessages();
} }
#endif // ENABLE_UI #endif // ENABLE_UI

View File

@ -70,14 +70,6 @@ Doc::Doc(Sprite* sprite)
Doc::~Doc() Doc::~Doc()
{ {
DOC_TRACE("DOC: Deleting", this); DOC_TRACE("DOC: Deleting", this);
try {
notify_observers<Doc*>(&DocObserver::onDestroy, this);
}
catch (...) {
LOG(ERROR, "DOC: Exception on DocObserver::onDestroy()\n");
}
removeFromContext(); removeFromContext();
} }
@ -592,6 +584,13 @@ Doc* Doc::duplicate(DuplicateType type) const
void Doc::close() void Doc::close()
{ {
try {
notify_observers<Doc*>(&DocObserver::onCloseDocument, this);
}
catch (...) {
LOG(ERROR, "DOC: Exception on DocObserver::onCloseDocument()\n");
}
removeFromContext(); removeFromContext();
} }

View File

@ -17,7 +17,7 @@ namespace app {
public: public:
virtual ~DocObserver() { } virtual ~DocObserver() { }
virtual void onDestroy(Doc* doc) { } virtual void onCloseDocument(Doc* doc) { }
virtual void onFileNameChanged(Doc* doc) { } virtual void onFileNameChanged(Doc* doc) { }
// General update. If an observer receives this event, it's because // General update. If an observer receives this event, it's because

View File

@ -506,8 +506,11 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
switch (msg->type()) { switch (msg->type()) {
case kCloseDisplayMessage: case kCloseDisplayMessage:
// When the main display is closed... // Only call the exit command/close the app when the the main
if (msg->display() == this->display()) { // display is the closed window in this kCloseDisplayMessage
// message and it's the current running foreground window.
if (msg->display() == this->display() &&
getForegroundWindow() == App::instance()->mainWindow()) {
// Execute the "Exit" command. // Execute the "Exit" command.
Command* command = Commands::instance()->byId(CommandId::Exit()); Command* command = Commands::instance()->byId(CommandId::Exit());
UIContext::instance()->executeCommandFromMenuOrShortcut(command); UIContext::instance()->executeCommandFromMenuOrShortcut(command);

View File

@ -14,6 +14,7 @@
#include "app/doc.h" #include "app/doc.h"
#include "app/doc_undo.h" #include "app/doc_undo.h"
#include "app/doc_undo_observer.h" #include "app/doc_undo_observer.h"
#include "app/pref/preferences.h"
#include "app/script/docobj.h" #include "app/script/docobj.h"
#include "app/script/engine.h" #include "app/script/engine.h"
#include "app/script/luacpp.h" #include "app/script/luacpp.h"
@ -122,7 +123,7 @@ private:
class AppEvents : public Events class AppEvents : public Events
, private ContextObserver { , private ContextObserver {
public: public:
enum : EventType { Unknown = -1, SiteChange }; enum : EventType { Unknown = -1, SiteChange, FgColorChange, BgColorChange };
AppEvents() { AppEvents() {
} }
@ -130,6 +131,10 @@ public:
EventType eventType(const char* eventName) const { EventType eventType(const char* eventName) const {
if (std::strcmp(eventName, "sitechange") == 0) if (std::strcmp(eventName, "sitechange") == 0)
return SiteChange; return SiteChange;
else if (std::strcmp(eventName, "fgcolorchange") == 0)
return FgColorChange;
else if (std::strcmp(eventName, "bgcolorchange") == 0)
return BgColorChange;
else else
return Unknown; return Unknown;
} }
@ -138,24 +143,49 @@ private:
void onAddFirstListener(EventType eventType) override { void onAddFirstListener(EventType eventType) override {
switch (eventType) { switch (eventType) {
case SiteChange: { case SiteChange:
App::instance()->context()->add_observer(this); App::instance()->context()->add_observer(this);
break; break;
} case FgColorChange:
m_fgConn = Preferences::instance().colorBar.fgColor
.AfterChange.connect([this]{ onFgColorChange(); });
break;
case BgColorChange:
m_bgConn = Preferences::instance().colorBar.bgColor
.AfterChange.connect([this]{ onBgColorChange(); });
break;
} }
} }
void onRemoveLastListener(EventType eventType) override { void onRemoveLastListener(EventType eventType) override {
switch (eventType) { switch (eventType) {
case SiteChange: { case SiteChange:
App::instance()->context()->remove_observer(this); App::instance()->context()->remove_observer(this);
break; break;
} case FgColorChange:
m_fgConn.disconnect();
break;
case BgColorChange:
m_bgConn.disconnect();
break;
} }
} }
void onFgColorChange() {
call(FgColorChange);
}
void onBgColorChange() {
call(BgColorChange);
}
// ContextObserver impl // ContextObserver impl
void onActiveSiteChange(const Site& site) override { call(SiteChange); } void onActiveSiteChange(const Site& site) override {
call(SiteChange);
}
obs::scoped_connection m_fgConn;
obs::scoped_connection m_bgConn;
}; };
class SpriteEvents : public Events class SpriteEvents : public Events
@ -170,11 +200,12 @@ public:
} }
~SpriteEvents() { ~SpriteEvents() {
if (m_observingUndo) { auto doc = this->doc();
doc()->undoHistory()->remove_observer(this); ASSERT(doc);
m_observingUndo = false; if (doc) {
disconnectFromUndoHistory(doc);
doc->remove_observer(this);
} }
doc()->remove_observer(this);
} }
EventType eventType(const char* eventName) const { EventType eventType(const char* eventName) const {
@ -185,11 +216,13 @@ public:
} }
// DocObserver impl // DocObserver impl
void onDestroy(Doc* doc) override { void onCloseDocument(Doc* doc) override {
auto it = g_spriteEvents.find(m_spriteId); auto it = g_spriteEvents.find(m_spriteId);
ASSERT(it != g_spriteEvents.end()); ASSERT(it != g_spriteEvents.end());
if (it != g_spriteEvents.end()) if (it != g_spriteEvents.end()) {
// As this is an unique_ptr, here we are calling ~SpriteEvents()
g_spriteEvents.erase(it); g_spriteEvents.erase(it);
}
} }
// DocUndoObserver impl // DocUndoObserver impl
@ -211,10 +244,7 @@ private:
void onRemoveLastListener(EventType eventType) override { void onRemoveLastListener(EventType eventType) override {
switch (eventType) { switch (eventType) {
case Change: { case Change: {
if (m_observingUndo) { disconnectFromUndoHistory(doc());
doc()->undoHistory()->remove_observer(this);
m_observingUndo = false;
}
break; break;
} }
} }
@ -228,6 +258,13 @@ private:
return nullptr; return nullptr;
} }
void disconnectFromUndoHistory(Doc* doc) {
if (m_observingUndo) {
doc->undoHistory()->remove_observer(this);
m_observingUndo = false;
}
}
ObjectId m_spriteId; ObjectId m_spriteId;
bool m_observingUndo = false; bool m_observingUndo = false;
}; };

View File

@ -13,11 +13,14 @@
#include "app/script/engine.h" #include "app/script/engine.h"
#include "app/script/luacpp.h" #include "app/script/luacpp.h"
#include "app/script/security.h" #include "app/script/security.h"
#include "ui/timer.h"
#include "ui/manager.h"
#include "ui/system.h" #include "ui/system.h"
#include <ixwebsocket/IXNetSystem.h> #include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXWebSocket.h> #include <ixwebsocket/IXWebSocket.h>
#include <sstream> #include <sstream>
#include <set>
namespace app { namespace app {
namespace script { namespace script {
@ -27,6 +30,18 @@ namespace {
// Additional "enum" value to make message callback simpler // Additional "enum" value to make message callback simpler
#define MESSAGE_TYPE_BINARY ((int)ix::WebSocketMessageType::Fragment + 10) #define MESSAGE_TYPE_BINARY ((int)ix::WebSocketMessageType::Fragment + 10)
static std::unique_ptr<ui::Timer> g_timer;
static std::set<ix::WebSocket*> g_connections;
static void close_ws(ix::WebSocket* ws)
{
ws->stop();
g_connections.erase(ws);
if (g_connections.empty())
g_timer.reset();
}
int WebSocket_new(lua_State* L) int WebSocket_new(lua_State* L)
{ {
static std::once_flag f; static std::once_flag f;
@ -55,7 +70,19 @@ int WebSocket_new(lua_State* L)
} }
lua_pop(L, 1); lua_pop(L, 1);
int type = lua_getfield(L, 1, "onreceive"); int type = lua_getfield(L, 1, "minreconnectwait");
if (type == LUA_TNUMBER) {
ws->setMinWaitBetweenReconnectionRetries(1000 * lua_tonumber(L, -1));
}
lua_pop(L, 1);
type = lua_getfield(L, 1, "maxreconnectwait");
if (type == LUA_TNUMBER) {
ws->setMaxWaitBetweenReconnectionRetries(1000 * lua_tonumber(L, -1));
}
lua_pop(L, 1);
type = lua_getfield(L, 1, "onreceive");
if (type == LUA_TFUNCTION) { if (type == LUA_TFUNCTION) {
int onreceiveRef = luaL_ref(L, LUA_REGISTRYINDEX); int onreceiveRef = luaL_ref(L, LUA_REGISTRYINDEX);
@ -80,6 +107,8 @@ int WebSocket_new(lua_State* L)
}); });
} }
else { else {
// Set a default handler to avoid a std::bad_function_call exception
ws->setOnMessageCallback([](const ix::WebSocketMessagePtr& msg) { });
lua_pop(L, 1); lua_pop(L, 1);
} }
} }
@ -90,7 +119,7 @@ int WebSocket_new(lua_State* L)
int WebSocket_gc(lua_State* L) int WebSocket_gc(lua_State* L)
{ {
auto ws = get_ptr<ix::WebSocket>(L, 1); auto ws = get_ptr<ix::WebSocket>(L, 1);
ws->stop(); close_ws(ws);
delete ws; delete ws;
return 0; return 0;
} }
@ -151,13 +180,24 @@ int WebSocket_connect(lua_State* L)
{ {
auto ws = get_ptr<ix::WebSocket>(L, 1); auto ws = get_ptr<ix::WebSocket>(L, 1);
ws->start(); ws->start();
if (g_connections.empty()) {
#ifdef ENABLE_UI
if (App::instance()->isGui()) {
g_timer = std::make_unique<ui::Timer>(33, ui::Manager::getDefault());
g_timer->start();
}
#endif
}
g_connections.insert(ws);
return 0; return 0;
} }
int WebSocket_close(lua_State* L) int WebSocket_close(lua_State* L)
{ {
auto ws = get_ptr<ix::WebSocket>(L, 1); auto ws = get_ptr<ix::WebSocket>(L, 1);
ws->stop(); close_ws(ws);
return 0; return 0;
} }
@ -198,14 +238,14 @@ void register_websocket_class(lua_State* L)
lua_newtable(L); lua_newtable(L);
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
lua_setglobal(L, "WebSocketMessageType"); lua_setglobal(L, "WebSocketMessageType");
setfield_integer(L, "Text", (int)ix::WebSocketMessageType::Message); setfield_integer(L, "TEXT", (int)ix::WebSocketMessageType::Message);
setfield_integer(L, "Binary", MESSAGE_TYPE_BINARY); setfield_integer(L, "BINARY", MESSAGE_TYPE_BINARY);
setfield_integer(L, "Open", (int)ix::WebSocketMessageType::Open); setfield_integer(L, "OPEN", (int)ix::WebSocketMessageType::Open);
setfield_integer(L, "Close", (int)ix::WebSocketMessageType::Close); setfield_integer(L, "CLOSE", (int)ix::WebSocketMessageType::Close);
setfield_integer(L, "Error", (int)ix::WebSocketMessageType::Error); setfield_integer(L, "ERROR", (int)ix::WebSocketMessageType::Error);
setfield_integer(L, "Ping", (int)ix::WebSocketMessageType::Ping); setfield_integer(L, "PING", (int)ix::WebSocketMessageType::Ping);
setfield_integer(L, "Pong", (int)ix::WebSocketMessageType::Pong); setfield_integer(L, "PONG", (int)ix::WebSocketMessageType::Pong);
setfield_integer(L, "Fragment", (int)ix::WebSocketMessageType::Fragment); setfield_integer(L, "FRAGMENT", (int)ix::WebSocketMessageType::Fragment);
lua_pop(L, 1); lua_pop(L, 1);
} }

View File

@ -394,10 +394,12 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
else if (elem_name == "slider") { else if (elem_name == "slider") {
const char *min = elem->Attribute("min"); const char *min = elem->Attribute("min");
const char *max = elem->Attribute("max"); const char *max = elem->Attribute("max");
const bool readonly = bool_attr(elem, "readonly", false);
int min_value = (min ? strtol(min, nullptr, 10): 0); int min_value = (min ? strtol(min, nullptr, 10): 0);
int max_value = (max ? strtol(max, nullptr, 10): 0); int max_value = (max ? strtol(max, nullptr, 10): 0);
widget = new Slider(min_value, max_value, min_value); widget = new Slider(min_value, max_value, min_value);
static_cast<Slider*>(widget)->setReadOnly(readonly);
} }
else if (elem_name == "textbox") { else if (elem_name == "textbox") {
const char* text = (elem->GetText() ? elem->GetText(): ""); const char* text = (elem->GetText() ? elem->GetText(): "");

View File

@ -133,6 +133,9 @@ static Item convert_to_item(TiXmlElement* elem)
if (name == "listbox") if (name == "listbox")
return item.typeIncl("ui::ListBox", return item.typeIncl("ui::ListBox",
"ui/listbox.h"); "ui/listbox.h");
if (name == "listitem")
return item.typeIncl("ui::ListItem",
"ui/listitem.h");
if (name == "panel") if (name == "panel")
return item.typeIncl("ui::Panel", return item.typeIncl("ui::Panel",
"ui/panel.h"); "ui/panel.h");