mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-14 04:19:12 +00:00
Run Console in background so commands can continue running and logging to the console if needed (fix #3227)
This commit is contained in:
parent
94065571b5
commit
cc7da16691
@ -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
|
||||
|
||||
|
@ -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, ...)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user