1fd5a6555e
Currently when input panel is using overlay mode and the cursor rectangle is below or above the screen area, the input panel may be placed off the screen. The change ensure it is always placed within the screen area using similar math like xdg_popup's slide_y constrain.
206 lines
6.7 KiB
C++
206 lines
6.7 KiB
C++
/*
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "inputpanelv1window.h"
|
|
#include "core/output.h"
|
|
#include "inputmethod.h"
|
|
#include "wayland/output.h"
|
|
#include "wayland/seat.h"
|
|
#include "wayland/surface.h"
|
|
#include "wayland/textinput_v1.h"
|
|
#include "wayland/textinput_v2.h"
|
|
#include "wayland/textinput_v3.h"
|
|
#include "wayland_server.h"
|
|
#include "workspace.h"
|
|
|
|
namespace KWin
|
|
{
|
|
|
|
InputPanelV1Window::InputPanelV1Window(InputPanelSurfaceV1Interface *panelSurface)
|
|
: WaylandWindow(panelSurface->surface())
|
|
, m_panelSurface(panelSurface)
|
|
{
|
|
setSkipSwitcher(true);
|
|
setSkipPager(true);
|
|
setSkipTaskbar(true);
|
|
|
|
connect(surface(), &SurfaceInterface::aboutToBeDestroyed, this, &InputPanelV1Window::destroyWindow);
|
|
connect(surface(), &SurfaceInterface::sizeChanged, this, &InputPanelV1Window::reposition);
|
|
connect(surface(), &SurfaceInterface::inputChanged, this, &InputPanelV1Window::reposition);
|
|
connect(surface(), &SurfaceInterface::mapped, this, &InputPanelV1Window::handleMapped);
|
|
|
|
connect(panelSurface, &InputPanelSurfaceV1Interface::topLevel, this, &InputPanelV1Window::showTopLevel);
|
|
connect(panelSurface, &InputPanelSurfaceV1Interface::overlayPanel, this, &InputPanelV1Window::showOverlayPanel);
|
|
connect(panelSurface, &InputPanelSurfaceV1Interface::aboutToBeDestroyed, this, &InputPanelV1Window::destroyWindow);
|
|
|
|
connect(workspace(), &Workspace::outputsChanged, this, &InputPanelV1Window::reposition);
|
|
|
|
kwinApp()->inputMethod()->setPanel(this);
|
|
}
|
|
|
|
void InputPanelV1Window::showOverlayPanel()
|
|
{
|
|
m_mode = Mode::Overlay;
|
|
maybeShow();
|
|
}
|
|
|
|
void InputPanelV1Window::showTopLevel(OutputInterface *output, InputPanelSurfaceV1Interface::Position position)
|
|
{
|
|
m_mode = Mode::VirtualKeyboard;
|
|
maybeShow();
|
|
}
|
|
|
|
void InputPanelV1Window::allow()
|
|
{
|
|
m_allowed = true;
|
|
maybeShow();
|
|
}
|
|
|
|
void InputPanelV1Window::show()
|
|
{
|
|
m_virtualKeyboardShouldBeShown = true;
|
|
maybeShow();
|
|
}
|
|
|
|
void InputPanelV1Window::hide()
|
|
{
|
|
m_virtualKeyboardShouldBeShown = false;
|
|
if (readyForPainting() && m_mode != Mode::Overlay) {
|
|
setHidden(true);
|
|
}
|
|
}
|
|
|
|
void InputPanelV1Window::reposition()
|
|
{
|
|
if (!readyForPainting()) {
|
|
return;
|
|
}
|
|
|
|
switch (m_mode) {
|
|
case Mode::None: {
|
|
// should never happen
|
|
}; break;
|
|
case Mode::VirtualKeyboard: {
|
|
// maliit creates a fullscreen overlay so use the input shape as the window geometry.
|
|
m_windowGeometry = surface()->input().boundingRect();
|
|
|
|
const auto activeOutput = workspace()->activeOutput();
|
|
QRectF availableArea;
|
|
if (waylandServer()->isScreenLocked()) {
|
|
availableArea = workspace()->clientArea(FullScreenArea, this, activeOutput);
|
|
} else {
|
|
availableArea = workspace()->clientArea(MaximizeArea, this, activeOutput);
|
|
}
|
|
|
|
QRectF geo = m_windowGeometry;
|
|
geo.moveLeft(availableArea.left() + (availableArea.width() - geo.width()) / 2);
|
|
geo.moveBottom(availableArea.bottom());
|
|
|
|
moveResize(geo);
|
|
} break;
|
|
case Mode::Overlay: {
|
|
auto textInputSurface = waylandServer()->seat()->focusedTextInputSurface();
|
|
auto textWindow = waylandServer()->findWindow(textInputSurface);
|
|
QRect cursorRectangle;
|
|
auto textInputV1 = waylandServer()->seat()->textInputV1();
|
|
if (textInputV1 && textInputV1->isEnabled() && textInputV1->surface() == textInputSurface) {
|
|
cursorRectangle = textInputV1->cursorRectangle();
|
|
}
|
|
auto textInputV2 = waylandServer()->seat()->textInputV2();
|
|
if (textInputV2 && textInputV2->isEnabled() && textInputV2->surface() == textInputSurface) {
|
|
cursorRectangle = textInputV2->cursorRectangle();
|
|
}
|
|
auto textInputV3 = waylandServer()->seat()->textInputV3();
|
|
if (textInputV3 && textInputV3->isEnabled() && textInputV3->surface() == textInputSurface) {
|
|
cursorRectangle = textInputV3->cursorRectangle();
|
|
}
|
|
if (textWindow) {
|
|
cursorRectangle.translate(textWindow->bufferGeometry().topLeft().toPoint());
|
|
const QRectF screen = Workspace::self()->clientArea(PlacementArea, this, cursorRectangle.bottomLeft());
|
|
|
|
m_windowGeometry = QRectF(QPointF(0, 0), surface()->size());
|
|
|
|
// Reuse the similar logic like xdg popup
|
|
QRectF popupRect(popupOffset(cursorRectangle, Qt::BottomEdge | Qt::LeftEdge, Qt::RightEdge | Qt::BottomEdge, m_windowGeometry.size()), m_windowGeometry.size());
|
|
|
|
if (popupRect.left() < screen.left()) {
|
|
popupRect.moveLeft(screen.left());
|
|
}
|
|
if (popupRect.right() > screen.right()) {
|
|
popupRect.moveRight(screen.right());
|
|
}
|
|
if (popupRect.top() < screen.top() || popupRect.bottom() > screen.bottom()) {
|
|
auto flippedPopupRect =
|
|
QRectF(popupOffset(cursorRectangle, Qt::TopEdge | Qt::LeftEdge, Qt::RightEdge | Qt::TopEdge, m_windowGeometry.size()), m_windowGeometry.size());
|
|
|
|
// if it still doesn't fit we should continue with the unflipped version
|
|
if (flippedPopupRect.top() >= screen.top() && flippedPopupRect.bottom() <= screen.bottom()) {
|
|
popupRect.moveTop(flippedPopupRect.top());
|
|
}
|
|
}
|
|
if (popupRect.top() < screen.top()) {
|
|
popupRect.moveTop(screen.top());
|
|
}
|
|
if (popupRect.bottom() > screen.bottom()) {
|
|
popupRect.moveBottom(screen.bottom());
|
|
}
|
|
|
|
moveResize(popupRect);
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
void InputPanelV1Window::destroyWindow()
|
|
{
|
|
m_panelSurface->disconnect(this);
|
|
m_panelSurface->surface()->disconnect(this);
|
|
|
|
markAsDeleted();
|
|
|
|
Q_EMIT closed();
|
|
StackingUpdatesBlocker blocker(workspace());
|
|
waylandServer()->removeWindow(this);
|
|
|
|
unref();
|
|
}
|
|
|
|
NET::WindowType InputPanelV1Window::windowType(bool direct) const
|
|
{
|
|
return NET::Utility;
|
|
}
|
|
|
|
QRectF InputPanelV1Window::frameRectToBufferRect(const QRectF &rect) const
|
|
{
|
|
return QRectF(rect.topLeft() - m_windowGeometry.topLeft(), surface()->size());
|
|
}
|
|
|
|
void InputPanelV1Window::moveResizeInternal(const QRectF &rect, MoveResizeMode mode)
|
|
{
|
|
updateGeometry(rect);
|
|
}
|
|
|
|
void InputPanelV1Window::handleMapped()
|
|
{
|
|
maybeShow();
|
|
}
|
|
|
|
void InputPanelV1Window::maybeShow()
|
|
{
|
|
const bool shouldShow = m_mode == Mode::Overlay || (m_mode == Mode::VirtualKeyboard && m_allowed && m_virtualKeyboardShouldBeShown);
|
|
if (shouldShow && !isDeleted() && surface()->isMapped()) {
|
|
markAsMapped();
|
|
reposition();
|
|
setHidden(false);
|
|
}
|
|
}
|
|
|
|
} // namespace KWin
|
|
|
|
#include "moc_inputpanelv1window.cpp"
|