mirror of https://github.com/aseprite/aseprite.git
375 lines
8.8 KiB
C++
375 lines
8.8 KiB
C++
// Aseprite UI Library
|
|
// Copyright (C) 2001-2018 David Capello
|
|
//
|
|
// This file is released under the terms of the MIT license.
|
|
// Read LICENSE.txt for more information.
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "ui/system.h"
|
|
|
|
#include "base/thread.h"
|
|
#include "gfx/point.h"
|
|
#include "os/display.h"
|
|
#include "os/surface.h"
|
|
#include "os/system.h"
|
|
#include "ui/clipboard_delegate.h"
|
|
#include "ui/cursor.h"
|
|
#include "ui/intern.h"
|
|
#include "ui/intern.h"
|
|
#include "ui/manager.h"
|
|
#include "ui/message.h"
|
|
#include "ui/overlay.h"
|
|
#include "ui/overlay_manager.h"
|
|
#include "ui/scale.h"
|
|
#include "ui/theme.h"
|
|
#include "ui/widget.h"
|
|
|
|
namespace ui {
|
|
|
|
// This is used to check if calls to UI layer are made from the non-UI
|
|
// thread. (Which might be catastrofic.)
|
|
base::thread::native_handle_type main_gui_thread;
|
|
|
|
// Current mouse cursor type.
|
|
|
|
static CursorType mouse_cursor_type = kOutsideDisplay;
|
|
static const Cursor* mouse_cursor_custom = nullptr;
|
|
static const Cursor* mouse_cursor = nullptr;
|
|
static os::Display* mouse_display = nullptr;
|
|
static Overlay* mouse_cursor_overlay = nullptr;
|
|
static bool use_native_mouse_cursor = true;
|
|
static bool support_native_custom_cursor = false;
|
|
|
|
// Mouse information (button and position).
|
|
|
|
static volatile MouseButtons m_buttons;
|
|
static gfx::Point m_mouse_pos;
|
|
static int mouse_cursor_scale = 1;
|
|
|
|
static int mouse_scares = 0;
|
|
|
|
static void update_mouse_overlay(const Cursor* cursor)
|
|
{
|
|
mouse_cursor = cursor;
|
|
|
|
if (mouse_cursor && mouse_scares == 0) {
|
|
if (!mouse_cursor_overlay) {
|
|
mouse_cursor_overlay = new Overlay(
|
|
mouse_cursor->getSurface(),
|
|
get_mouse_position(),
|
|
Overlay::MouseZOrder);
|
|
|
|
OverlayManager::instance()->addOverlay(mouse_cursor_overlay);
|
|
}
|
|
else {
|
|
mouse_cursor_overlay->setSurface(mouse_cursor->getSurface());
|
|
update_cursor_overlay();
|
|
}
|
|
}
|
|
else if (mouse_cursor_overlay) {
|
|
OverlayManager::instance()->removeOverlay(mouse_cursor_overlay);
|
|
mouse_cursor_overlay->setSurface(NULL);
|
|
delete mouse_cursor_overlay;
|
|
mouse_cursor_overlay = NULL;
|
|
}
|
|
}
|
|
|
|
static bool update_custom_native_cursor(const Cursor* cursor)
|
|
{
|
|
bool result = false;
|
|
|
|
// Check if we can use a custom native mouse in this platform
|
|
if (!use_native_mouse_cursor &&
|
|
support_native_custom_cursor &&
|
|
mouse_display) {
|
|
if (cursor) {
|
|
result = mouse_display->setNativeMouseCursor(
|
|
// The surface is already scaled by guiscale()
|
|
cursor->getSurface(),
|
|
cursor->getFocus(),
|
|
// We scale the cursor by the os::Display scale
|
|
mouse_display->scale() * mouse_cursor_scale);
|
|
}
|
|
else if (mouse_cursor_type == kOutsideDisplay) {
|
|
result = mouse_display->setNativeMouseCursor(os::kArrowCursor);
|
|
}
|
|
else {
|
|
result = mouse_display->setNativeMouseCursor(os::kNoCursor);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void update_mouse_cursor()
|
|
{
|
|
os::NativeCursor nativeCursor = os::kNoCursor;
|
|
const Cursor* cursor = nullptr;
|
|
|
|
if (use_native_mouse_cursor ||
|
|
mouse_cursor_type == kOutsideDisplay) {
|
|
switch (mouse_cursor_type) {
|
|
case ui::kOutsideDisplay:
|
|
nativeCursor = os::kArrowCursor;
|
|
break;
|
|
case ui::kNoCursor: break;
|
|
case ui::kArrowCursor:
|
|
case ui::kArrowPlusCursor:
|
|
nativeCursor = os::kArrowCursor;
|
|
break;
|
|
case ui::kCrosshairCursor:
|
|
nativeCursor = os::kCrosshairCursor;
|
|
break;
|
|
case ui::kForbiddenCursor:
|
|
nativeCursor = os::kForbiddenCursor;
|
|
break;
|
|
case ui::kHandCursor:
|
|
nativeCursor = os::kLinkCursor;
|
|
break;
|
|
case ui::kScrollCursor:
|
|
case ui::kMoveCursor:
|
|
nativeCursor = os::kMoveCursor;
|
|
break;
|
|
case ui::kSizeNSCursor: nativeCursor = os::kSizeNSCursor; break;
|
|
case ui::kSizeWECursor: nativeCursor = os::kSizeWECursor; break;
|
|
case ui::kSizeNCursor: nativeCursor = os::kSizeNCursor; break;
|
|
case ui::kSizeNECursor: nativeCursor = os::kSizeNECursor; break;
|
|
case ui::kSizeECursor: nativeCursor = os::kSizeECursor; break;
|
|
case ui::kSizeSECursor: nativeCursor = os::kSizeSECursor; break;
|
|
case ui::kSizeSCursor: nativeCursor = os::kSizeSCursor; break;
|
|
case ui::kSizeSWCursor: nativeCursor = os::kSizeSWCursor; break;
|
|
case ui::kSizeWCursor: nativeCursor = os::kSizeWCursor; break;
|
|
case ui::kSizeNWCursor: nativeCursor = os::kSizeNWCursor; break;
|
|
}
|
|
}
|
|
|
|
// Set native cursor
|
|
if (mouse_display) {
|
|
bool ok = mouse_display->setNativeMouseCursor(nativeCursor);
|
|
|
|
// It looks like the specific native cursor is not supported,
|
|
// so we can should use the internal overlay (even when we
|
|
// have use_native_mouse_cursor flag enabled).
|
|
if (!ok)
|
|
nativeCursor = os::kNoCursor;
|
|
}
|
|
|
|
// Use a custom cursor
|
|
if (nativeCursor == os::kNoCursor &&
|
|
mouse_cursor_type != ui::kOutsideDisplay) {
|
|
if (get_theme() && mouse_cursor_type != ui::kCustomCursor)
|
|
cursor = get_theme()->getStandardCursor(mouse_cursor_type);
|
|
else
|
|
cursor = mouse_cursor_custom;
|
|
}
|
|
|
|
// Try to use a custom native cursor if it's possible
|
|
if (!update_custom_native_cursor(cursor)) {
|
|
// Or an overlay as last resource
|
|
update_mouse_overlay(cursor);
|
|
}
|
|
}
|
|
|
|
static UISystem* g_instance = nullptr;
|
|
|
|
// static
|
|
UISystem* UISystem::instance()
|
|
{
|
|
return g_instance;
|
|
}
|
|
|
|
UISystem::UISystem()
|
|
: m_clipboardDelegate(nullptr)
|
|
{
|
|
ASSERT(!g_instance);
|
|
g_instance = this;
|
|
|
|
main_gui_thread = base::this_thread::native_handle();
|
|
mouse_cursor_type = kOutsideDisplay;
|
|
support_native_custom_cursor =
|
|
((os::instance() &&
|
|
(int(os::instance()->capabilities()) &
|
|
int(os::Capabilities::CustomNativeMouseCursor))) ?
|
|
true: false);
|
|
|
|
details::initWidgets();
|
|
}
|
|
|
|
UISystem::~UISystem()
|
|
{
|
|
OverlayManager::destroyInstance();
|
|
|
|
// finish theme
|
|
set_theme(nullptr, guiscale());
|
|
|
|
details::exitWidgets();
|
|
|
|
_internal_set_mouse_display(nullptr);
|
|
if (!update_custom_native_cursor(nullptr))
|
|
update_mouse_overlay(nullptr);
|
|
|
|
ASSERT(g_instance == this);
|
|
g_instance = nullptr;
|
|
}
|
|
|
|
void _internal_set_mouse_display(os::Display* display)
|
|
{
|
|
CursorType cursor = get_mouse_cursor();
|
|
set_mouse_cursor(kNoCursor);
|
|
mouse_display = display;
|
|
if (display)
|
|
set_mouse_cursor(cursor); // Restore mouse cursor
|
|
}
|
|
|
|
int display_w()
|
|
{
|
|
if (mouse_display)
|
|
return mouse_display->width() / mouse_display->scale();
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
int display_h()
|
|
{
|
|
if (mouse_display)
|
|
return mouse_display->height() / mouse_display->scale();
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
void set_clipboard_text(const std::string& text)
|
|
{
|
|
ASSERT(g_instance);
|
|
ClipboardDelegate* delegate = g_instance->clipboardDelegate();
|
|
if (delegate)
|
|
delegate->setClipboardText(text);
|
|
}
|
|
|
|
bool get_clipboard_text(std::string& text)
|
|
{
|
|
ASSERT(g_instance);
|
|
ClipboardDelegate* delegate = g_instance->clipboardDelegate();
|
|
if (delegate)
|
|
return delegate->getClipboardText(text);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void update_cursor_overlay()
|
|
{
|
|
if (mouse_cursor_overlay != NULL && mouse_scares == 0) {
|
|
gfx::Point newPos =
|
|
get_mouse_position() - mouse_cursor->getFocus();
|
|
|
|
if (newPos != mouse_cursor_overlay->position()) {
|
|
mouse_cursor_overlay->moveOverlay(newPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
void set_use_native_cursors(bool state)
|
|
{
|
|
use_native_mouse_cursor = state;
|
|
update_mouse_cursor();
|
|
}
|
|
|
|
CursorType get_mouse_cursor()
|
|
{
|
|
return mouse_cursor_type;
|
|
}
|
|
|
|
void set_mouse_cursor(CursorType type, const Cursor* cursor)
|
|
{
|
|
if (mouse_cursor_type == type &&
|
|
mouse_cursor_custom == cursor)
|
|
return;
|
|
|
|
mouse_cursor_type = type;
|
|
mouse_cursor_custom = cursor;
|
|
update_mouse_cursor();
|
|
}
|
|
|
|
void set_mouse_cursor_scale(const int newScale)
|
|
{
|
|
mouse_cursor_scale = newScale;
|
|
update_mouse_cursor();
|
|
}
|
|
|
|
void hide_mouse_cursor()
|
|
{
|
|
ASSERT(mouse_scares >= 0);
|
|
mouse_scares++;
|
|
}
|
|
|
|
void show_mouse_cursor()
|
|
{
|
|
ASSERT(mouse_scares > 0);
|
|
mouse_scares--;
|
|
|
|
if (mouse_scares == 0)
|
|
update_mouse_cursor();
|
|
}
|
|
|
|
void _internal_no_mouse_position()
|
|
{
|
|
if (!update_custom_native_cursor(nullptr))
|
|
update_mouse_overlay(nullptr);
|
|
}
|
|
|
|
void _internal_set_mouse_position(const gfx::Point& newPos)
|
|
{
|
|
m_mouse_pos = newPos;
|
|
}
|
|
|
|
void _internal_set_mouse_buttons(MouseButtons buttons)
|
|
{
|
|
m_buttons = buttons;
|
|
}
|
|
|
|
MouseButtons _internal_get_mouse_buttons()
|
|
{
|
|
return m_buttons;
|
|
}
|
|
|
|
const gfx::Point& get_mouse_position()
|
|
{
|
|
return m_mouse_pos;
|
|
}
|
|
|
|
void set_mouse_position(const gfx::Point& newPos)
|
|
{
|
|
if (mouse_display)
|
|
mouse_display->setMousePosition(newPos);
|
|
|
|
_internal_set_mouse_position(newPos);
|
|
}
|
|
|
|
void execute_from_ui_thread(std::function<void()>&& f)
|
|
{
|
|
ASSERT(Manager::getDefault());
|
|
|
|
Manager* man = Manager::getDefault();
|
|
ASSERT(man);
|
|
|
|
FunctionMessage* msg = new FunctionMessage(std::move(f));
|
|
msg->addRecipient(man);
|
|
man->enqueueMessage(msg);
|
|
}
|
|
|
|
bool is_ui_thread()
|
|
{
|
|
return (main_gui_thread == base::this_thread::native_handle());
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
void assert_ui_thread()
|
|
{
|
|
ASSERT(is_ui_thread());
|
|
}
|
|
#endif
|
|
|
|
} // namespace ui
|