Run Console in background so commands can continue running and logging to the console if needed (fix #3227)

master
David Capello 2022-04-28 21:58:15 -03:00
parent 94065571b5
commit cc7da16691
5 changed files with 119 additions and 30 deletions

View File

@ -97,6 +97,7 @@ namespace {
class ConsoleEngineDelegate : public script::EngineDelegate {
public:
ConsoleEngineDelegate(Console& console) : m_console(console) { }
void onConsoleError(const char* text) override {
onConsolePrint(text);
}
@ -104,7 +105,7 @@ public:
m_console.printf("%s\n", text);
}
private:
Console m_console;
Console& m_console;
};
} // anonymous namespace
@ -497,7 +498,7 @@ void App::run()
Console console;
#ifdef ENABLE_SCRIPTING
// Use the app::Console() for script errors
ConsoleEngineDelegate delegate;
ConsoleEngineDelegate delegate(console);
script::ScopedEngineDelegate setEngineDelegate(m_engine.get(), &delegate);
#endif

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2018-2022 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -14,32 +14,50 @@
#include "app/app.h"
#include "app/context.h"
#include "app/modules/gui.h"
#include "app/ui/main_window.h"
#include "app/ui/status_bar.h"
#include "base/memory.h"
#include "base/string.h"
#include "ui/system.h"
#include "ui/ui.h"
#include <cstdarg>
#include <cstdio>
#include <memory>
#define TRACE_CON(...) // TRACEARGS(__VA_ARGS__)
namespace app {
using namespace ui;
class Console::ConsoleWindow : public Window {
Console::ConsoleWindow* Console::m_console = nullptr;
class Console::ConsoleWindow final : public Window {
public:
ConsoleWindow() : Window(Window::WithTitleBar, "Console"),
m_textbox("", WORDWRAP),
m_button("Cancel") {
TRACE_CON("CON: ConsoleWindow this=", this);
m_button.Click.connect([this]{ closeWindow(&m_button); });
// When the main window is closed, we should close the console (in
// other case the main message loop will continue running for the
// console too).
m_mainWindowClosedConn =
App::instance()->mainWindow()->Close.connect(
[this]{ closeWindow(nullptr); });
// When the window is closed, we clear the text
Close.connect(
[this]{
m_mainWindowClosedConn.disconnect();
m_textbox.setText(std::string());
Console::m_console->deferDelete();
Console::m_console = nullptr;
TRACE_CON("CON: Close signal");
});
m_view.attachToView(&m_textbox);
@ -56,17 +74,33 @@ public:
initTheme();
}
~ConsoleWindow() {
TRACE_CON("CON: ~ConsoleWindow this=", this);
}
void addMessage(const std::string& msg) {
if (!m_hasText) {
m_hasText = true;
centerConsole();
}
gfx::Size maxSize = m_view.getScrollableSize();
gfx::Size visible = m_view.visibleSize();
gfx::Point pt = m_view.viewScroll();
const bool autoScroll = (pt.y >= maxSize.h - visible.h);
m_textbox.setText(m_textbox.text() + msg);
if (autoScroll) {
maxSize = m_view.getScrollableSize();
visible = m_view.visibleSize();
pt.y = maxSize.h - visible.h;
m_view.setViewScroll(pt);
}
}
bool isConsoleVisible() const {
return (m_hasText && isVisible());
bool hasConsoleText() const {
return m_hasText;
}
void centerConsole() {
@ -78,19 +112,66 @@ public:
}
private:
// As Esc key activates the close button only on foreground windows,
// we have to override this method to allow pressing the window
// close button using Esc key even in this window (which runs in the
// background).
bool shouldProcessEscKeyToCloseWindow() const override {
return true;
}
bool onProcessMessage(ui::Message* msg) override {
switch (msg->type()) {
case ui::kKeyDownMessage:
case ui::kKeyDownMessage: {
auto scancode = static_cast<KeyMessage*>(msg)->scancode();
#if defined __APPLE__
if (msg->onlyCmdPressed())
#else
if (msg->onlyCtrlPressed())
#endif
{
if (static_cast<KeyMessage*>(msg)->scancode() == kKeyC)
if (scancode == kKeyC)
set_clipboard_text(m_textbox.text());
}
// Esc to close the window.
if (auto closeButton = this->closeButton()) {
bool p = msg->propagateToParent();
msg->setPropagateToParent(false);
if (closeButton->sendMessage(msg))
return true;
msg->setPropagateToParent(p);
}
// Send Enter key to the Close button, Tab to change focus
if ((scancode == kKeyEnter) ||
(scancode == kKeyEnterPad))
return m_button.sendMessage(msg);
if (scancode == kKeyTab) {
if (auto mgr = manager())
return mgr->processFocusMovementMessage(msg);
}
// All keys are used if we have this window focused (so they
// don't trigger commands)
return true;
}
case ui::kKeyUpMessage:
if (auto closeButton = this->closeButton()) {
bool p = msg->propagateToParent();
msg->setPropagateToParent(false);
if (closeButton->sendMessage(msg))
return true;
msg->setPropagateToParent(p);
}
break;
}
return Window::onProcessMessage(msg);
@ -102,18 +183,18 @@ private:
m_button.setMinSize(gfx::Size(60*ui::guiscale(), 0));
}
obs::scoped_connection m_mainWindowClosedConn;
View m_view;
TextBox m_textbox;
Button m_button;
bool m_hasText = false;
};
int Console::m_consoleCounter = 0;
std::unique_ptr<Console::ConsoleWindow> Console::m_console = nullptr;
Console::Console(Context* ctx)
: m_withUI(false)
{
TRACE_CON("CON: Console this=", this, "ctx=", ctx, "is_ui_thread=", ui::is_ui_thread(), "{");
if (!ui::is_ui_thread())
return;
@ -129,27 +210,26 @@ Console::Console(Context* ctx)
if (!m_withUI)
return;
++m_consoleCounter;
if (m_console || m_consoleCounter > 1)
return;
TRACE_CON("CON: -> withUI=", m_withUI);
m_console.reset(new ConsoleWindow);
if (!m_console)
m_console = new ConsoleWindow;
}
Console::~Console()
{
TRACE_CON("CON: } ~Console this=", this, "withUI=", m_withUI);
if (!m_withUI)
return;
--m_consoleCounter;
if (m_console && m_console->isConsoleVisible()) {
m_console->manager()->attractFocus(m_console.get());
m_console->openWindowInForeground();
if (m_console &&
m_console->hasConsoleText() &&
!m_console->isVisible()) {
m_console->manager()->attractFocus(m_console);
m_console->openWindow();
TRACE_CON("CON: openWindow");
}
if (m_consoleCounter == 0)
m_console.reset();
}
void Console::printf(const char* format, ...)

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2022 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -10,7 +10,6 @@
#pragma once
#include <exception>
#include <memory>
namespace app {
class Context;
@ -29,8 +28,7 @@ namespace app {
class ConsoleWindow;
bool m_withUI;
static int m_consoleCounter;
static std::unique_ptr<Console::ConsoleWindow> m_console;
static ConsoleWindow* m_console;
};
} // namespace app

View File

@ -80,7 +80,7 @@ protected:
return true;
case kKeyDownMessage:
if (window()->isForeground() &&
if (window()->shouldProcessEscKeyToCloseWindow() &&
static_cast<KeyMessage*>(msg)->scancode() == kKeyEsc) {
setSelected(true);
return true;
@ -88,7 +88,7 @@ protected:
break;
case kKeyUpMessage:
if (window()->isForeground() &&
if (window()->shouldProcessEscKeyToCloseWindow() &&
static_cast<KeyMessage*>(msg)->scancode() == kKeyEsc) {
if (isSelected()) {
setSelected(false);

View File

@ -1,4 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2022 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
@ -54,10 +55,19 @@ namespace ui {
HitTest hitTest(const gfx::Point& point);
// Esc key closes the current window (presses the little close
// button decorator) only on foreground windows, but you can
// override this to allow this behavior in other kind of windows.
virtual bool shouldProcessEscKeyToCloseWindow() const {
return isForeground();
}
// Signals
obs::signal<void (CloseEvent&)> Close;
protected:
ButtonBase* closeButton() { return m_closeButton; }
virtual bool onProcessMessage(Message* msg) override;
virtual void onResize(ResizeEvent& ev) override;
virtual void onSizeHint(SizeHintEvent& ev) override;