openclonk/src/editor/C4ConsoleQtViewport.cpp

403 lines
12 KiB
C++

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2013, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
/* Player and editor viewports in console */
#include <C4Include.h>
#include <C4Value.h>
#include <C4ConsoleQtViewport.h>
#include <C4ConsoleQtState.h>
#include <C4Viewport.h>
#include <C4ViewportWindow.h>
#include <C4Console.h>
#include <C4MouseControl.h>
/* Console viewports */
C4ConsoleQtViewportView::C4ConsoleQtViewportView(class C4ConsoleQtViewportDockWidget *dock)
: QOpenGLWidget(dock), dock(dock), cvp(dock->cvp ? dock->cvp->cvp : NULL)
{
setAutoFillBackground(false);
setAttribute(Qt::WA_NoSystemBackground, true);
#ifdef USE_WIN32_WINDOWS
setAttribute(Qt::WA_NativeWindow, true);
#endif
setAttribute(Qt::WA_ShowWithoutActivating, true);
setWindowFlags(Qt::FramelessWindowHint);
setFocusPolicy(Qt::WheelFocus);
setMouseTracking(true);
// Register for viewport
C4ViewportWindow *window = dock->cvp;
#ifdef USE_WIN32_WINDOWS
window->hWindow = reinterpret_cast<HWND>(this->winId());
#else
window->glwidget = this;
#endif
}
bool C4ConsoleQtViewportView::IsPlayViewport() const
{
return (cvp && ::MouseControl.IsViewport(cvp)
&& (::Console.EditCursor.GetMode() == C4CNS_ModePlay));
}
bool C4ConsoleQtViewportView::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
// Handle native Windows messages
#ifdef USE_WIN32_WINDOWS
MSG *msg = static_cast<MSG*>(message);
switch (msg->message)
{
//----------------------------------------------------------------------------------------------------------------------------------
case WM_HSCROLL:
switch (LOWORD(msg->wParam))
{
case SB_THUMBTRACK:
case SB_THUMBPOSITION: cvp->SetViewX(float(HIWORD(msg->wParam)) / cvp->GetZoom()); break;
case SB_LINELEFT: cvp->ScrollView(-ViewportScrollSpeed, 0.0f); break;
case SB_LINERIGHT: cvp->ScrollView(+ViewportScrollSpeed, 0.0f); break;
case SB_PAGELEFT: cvp->ScrollView(-cvp->ViewWdt / cvp->GetZoom(), 0.0f); break;
case SB_PAGERIGHT: cvp->ScrollView(+cvp->ViewWdt / cvp->GetZoom(), 0.0f); break;
}
cvp->Execute();
cvp->ScrollBarsByViewPosition();
return true;
//----------------------------------------------------------------------------------------------------------------------------------
case WM_VSCROLL:
switch (LOWORD(msg->wParam))
{
case SB_THUMBTRACK:
case SB_THUMBPOSITION: cvp->SetViewY(float(HIWORD(msg->wParam)) / cvp->GetZoom()); break;
case SB_LINEUP: cvp->ScrollView(0.0f, -ViewportScrollSpeed); break;
case SB_LINEDOWN: cvp->ScrollView(0.0f, +ViewportScrollSpeed); break;
case SB_PAGEUP: cvp->ScrollView(0.0f, -cvp->ViewWdt / cvp->GetZoom()); break;
case SB_PAGEDOWN: cvp->ScrollView(0.0f, +cvp->ViewWdt / cvp->GetZoom()); break;
}
cvp->Execute();
cvp->ScrollBarsByViewPosition();
return true;
//----------------------------------------------------------------------------------------------------------------------------------
}
#endif
return false;
}
// Get Shift state as Win32 wParam
uint32_t GetShiftWParam()
{
auto modifiers = QGuiApplication::keyboardModifiers();
uint32_t result = 0;
if (modifiers & Qt::ShiftModifier) result |= MK_SHIFT;
if (modifiers & Qt::ControlModifier) result |= MK_CONTROL;
if (modifiers & Qt::AltModifier) result |= MK_ALT;
return result;
}
void C4ConsoleQtViewportView::mouseMoveEvent(QMouseEvent *eventMove)
{
if (IsPlayViewport())
{
bool is_in_drawrange = (Inside<int32_t>(eventMove->x() - cvp->DrawX, 0, cvp->ViewWdt - 1)
&& Inside<int32_t>(eventMove->y() - cvp->DrawY, 0, cvp->ViewHgt - 1));
this->setCursor(is_in_drawrange ? Qt::BlankCursor : Qt::CrossCursor);
C4GUI::MouseMove(C4MC_Button_None, eventMove->x(), eventMove->y(), GetShiftWParam(), cvp);
}
else
{
this->setCursor(Qt::CrossCursor);
cvp->pWindow->EditCursorMove(eventMove->x(), eventMove->y(), GetShiftWParam());
}
}
void C4ConsoleQtViewportView::mousePressEvent(QMouseEvent *eventPress)
{
if (IsPlayViewport())
{
int32_t btn = C4MC_Button_None;
switch (eventPress->button())
{
case Qt::LeftButton: btn = C4MC_Button_LeftDown; break;
case Qt::RightButton: btn = C4MC_Button_RightDown; break;
}
C4GUI::MouseMove(btn, eventPress->x(), eventPress->y(), GetShiftWParam(), cvp);
}
else
{
// movement update needed before, so target is always up-to-date
cvp->pWindow->EditCursorMove(eventPress->x(), eventPress->y(), GetShiftWParam());
switch (eventPress->button())
{
case Qt::LeftButton: ::Console.EditCursor.LeftButtonDown(GetShiftWParam()); break;
case Qt::RightButton: ::Console.EditCursor.RightButtonDown(GetShiftWParam()); break;
}
}
}
void C4ConsoleQtViewportView::mouseDoubleClickEvent(QMouseEvent *eventPress)
{
if (IsPlayViewport())
{
int32_t btn = C4MC_Button_None;
switch (eventPress->button())
{
case Qt::LeftButton: btn = C4MC_Button_LeftDouble; break;
case Qt::RightButton: btn = C4MC_Button_RightDouble; break;
}
C4GUI::MouseMove(btn, eventPress->x(), eventPress->y(), GetShiftWParam(), cvp);
}
}
void C4ConsoleQtViewportView::mouseReleaseEvent(QMouseEvent *releaseEvent)
{
if (IsPlayViewport())
{
int32_t btn = C4MC_Button_None;
switch (releaseEvent->button())
{
case Qt::LeftButton: btn = C4MC_Button_LeftUp; break;
case Qt::RightButton: btn = C4MC_Button_RightUp; break;
}
C4GUI::MouseMove(btn, releaseEvent->x(), releaseEvent->y(), GetShiftWParam(), cvp);
}
else
{
switch (releaseEvent->button())
{
case Qt::LeftButton: ::Console.EditCursor.LeftButtonUp(GetShiftWParam()); break;
case Qt::RightButton: ::Console.EditCursor.RightButtonUp(GetShiftWParam()); break;
}
}
}
void C4ConsoleQtViewportView::wheelEvent(QWheelEvent *event)
{
if (IsPlayViewport())
{
int delta = event->delta() / 8;
if (!delta) delta = event->delta(); // abs(delta)<8?
uint32_t shift = (delta>0) ? (delta<<16) : uint32_t(delta<<16);
C4GUI::MouseMove(C4MC_Button_Wheel, event->x(), event->y(), shift, cvp);
}
else
{
// TODO zoom?
}
}
void C4ConsoleQtViewportView::focusInEvent(QFocusEvent * event)
{
dock->OnActiveChanged(true);
QWidget::focusInEvent(event);
}
void C4ConsoleQtViewportView::focusOutEvent(QFocusEvent * event)
{
dock->OnActiveChanged(false);
QWidget::focusOutEvent(event);
}
/* Keyboard scan code mapping from Qt to our keys */
/** Convert certain keys to (unix(?)) scancodes (those that differ from scancodes on Windows. Sometimes. Maybe.) */
static C4KeyCode QtKeyToUnixScancode(const QKeyEvent &event)
{
//LogF("VK: %x SC: %x key: %x", event.nativeVirtualKey(), event.nativeScanCode(), event.key());
// Map some special keys
switch (event.key())
{
case Qt::Key_Home: return K_HOME;
case Qt::Key_End: return K_END;
case Qt::Key_PageUp: return K_PAGEUP;
case Qt::Key_PageDown: return K_PAGEDOWN;
case Qt::Key_Up: return K_UP;
case Qt::Key_Down: return K_DOWN;
case Qt::Key_Left: return K_LEFT;
case Qt::Key_Right: return K_RIGHT;
/* case Qt::Key_Clear: return K_CENTER; */
case Qt::Key_Insert: return K_INSERT;
case Qt::Key_Delete: return K_DELETE;
case Qt::Key_Menu: return K_MENU;
case Qt::Key_Pause: return K_PAUSE;
case Qt::Key_Print: return K_PRINT;
case Qt::Key_NumLock: return K_NUM;
case Qt::Key_ScrollLock:return K_SCROLL;
default:
// Some native Win32 key mappings...
#ifdef USE_WIN32_WINDOWS
switch (event.nativeVirtualKey())
{
case VK_LWIN: return K_WIN_L;
case VK_RWIN: return K_WIN_R;
case VK_NUMPAD1: return K_NUM1;
case VK_NUMPAD2: return K_NUM2;
case VK_NUMPAD3: return K_NUM3;
case VK_NUMPAD4: return K_NUM4;
case VK_NUMPAD5: return K_NUM5;
case VK_NUMPAD6: return K_NUM6;
case VK_NUMPAD7: return K_NUM7;
case VK_NUMPAD8: return K_NUM8;
case VK_NUMPAD9: return K_NUM9;
case VK_NUMPAD0: return K_NUM0;
}
switch (event.nativeScanCode())
{
case 285: return K_CONTROL_R;
}
#endif
// Otherwise rely on native scan code to be the same on all platforms
return event.nativeScanCode();
}
}
void C4ConsoleQtViewportView::keyPressEvent(QKeyEvent * event)
{
// Convert key to our internal mapping
C4KeyCode code = QtKeyToUnixScancode(*event);
// Viewport-only handling
bool handled = false;
if (code == K_SCROLL)
{
cvp->TogglePlayerLock();
handled = true;
}
// Handled if handled as player control or main editor
if (!handled) handled = Game.DoKeyboardInput(code, KEYEV_Down, !!(event->modifiers() & Qt::AltModifier), !!(event->modifiers() & Qt::ControlModifier), !!(event->modifiers() & Qt::ShiftModifier), event->isAutoRepeat(), NULL);
if (!handled) handled = dock->main_window->HandleEditorKeyDown(event);
event->setAccepted(handled);
}
void C4ConsoleQtViewportView::keyReleaseEvent(QKeyEvent * event)
{
// Convert key to our internal mapping
C4KeyCode code = QtKeyToUnixScancode(*event);
// Handled if handled as player control
bool handled = Game.DoKeyboardInput(code, KEYEV_Up, !!(event->modifiers() & Qt::AltModifier), !!(event->modifiers() & Qt::ControlModifier), !!(event->modifiers() & Qt::ShiftModifier), event->isAutoRepeat(), NULL);
if (!handled) handled = dock->main_window->HandleEditorKeyUp(event);
event->setAccepted(handled);
}
void C4ConsoleQtViewportView::enterEvent(QEvent *)
{
// TODO: This should better be managed by the viewport
// looks weird when there's multiple viewports open
// but for some reason, the EditCursor drawing stuff is not associated with the viewport (yet)
::Console.EditCursor.SetMouseHover(true);
}
void C4ConsoleQtViewportView::leaveEvent(QEvent *)
{
// TODO: This should better be managed by the viewport
::Console.EditCursor.SetMouseHover(false);
}
void C4ConsoleQtViewportView::initializeGL() { }
void C4ConsoleQtViewportView::resizeGL(int w, int h)
{
cvp->UpdateOutputSize(w, h);
}
void C4ConsoleQtViewportView::paintGL()
{
// Painting is done regularily elsewhere anyways.
/* cvp->Execute(); */
}
C4ConsoleQtViewportLabel::C4ConsoleQtViewportLabel(const QString &title, C4ConsoleQtViewportDockWidget *dock)
: QLabel(dock), dock(dock)
{
OnActiveChanged(false);
setText(title);
}
void C4ConsoleQtViewportLabel::OnActiveChanged(bool active)
{
// set color schemes for inactive / active viewport headers
QColor bgclr = QApplication::palette(this).color(QPalette::Highlight);
QColor fontclr = QApplication::palette(this).color(QPalette::HighlightedText);
if (active)
setStyleSheet(QString(
"QLabel { background: %1; padding: 3px; color: %2; font-weight: bold; }")
.arg(bgclr.name(), fontclr.name()));
else
setStyleSheet(QString(
"QLabel { padding: 3px; }"));
}
void C4ConsoleQtViewportLabel::mousePressEvent(QMouseEvent *eventPress)
{
dock->view->setFocus();
QLabel::mousePressEvent(eventPress);
}
C4ConsoleQtViewportDockWidget::C4ConsoleQtViewportDockWidget(C4ConsoleQtMainWindow *main_window, QMainWindow *parent, C4ViewportWindow *cvp)
: QDockWidget("", parent), main_window(main_window), cvp(cvp)
{
// Translated title
setWindowTitle(LoadResStr("IDS_CNS_VIEWPORT"));
// Actual view container
view = new C4ConsoleQtViewportView(this);
setTitleBarWidget((title_label = new C4ConsoleQtViewportLabel(windowTitle(), this)));
setWidget(view);
connect(this, SIGNAL(topLevelChanged(bool)), this, SLOT(TopLevelChanged(bool)));
}
void C4ConsoleQtViewportDockWidget::mousePressEvent(QMouseEvent *eventPress)
{
// Clicking the dock focuses the viewport
view->setFocus();
QDockWidget::mousePressEvent(eventPress);
}
void C4ConsoleQtViewportDockWidget::OnActiveChanged(bool active)
{
title_label->OnActiveChanged(active);
}
void C4ConsoleQtViewportDockWidget::TopLevelChanged(bool is_floating)
{
if (!is_floating)
{
// docked has custom title
setTitleBarWidget(title_label);
}
else
{
// undocked using OS title
setTitleBarWidget(NULL);
}
// Ensure focus after undock and after re-docking floating viewport window
view->setFocus();
}
void C4ConsoleQtViewportDockWidget::closeEvent(QCloseEvent * event)
{
QDockWidget::closeEvent(event);
if (event->isAccepted())
{
if (cvp) cvp->Close();
cvp = NULL;
deleteLater();
}
}