7292af3d04
With fractional scaling integer based logical geometry may not match device pixels. Once we have a floating point base we can fix that. This also is important for our X11 scale override, with a scale of 2 we could get logical sizes with halves. We already have all input being floating point, this doubles down on it for all remaining geometry. - Outputs remain integer to ensure that any screen on the right remains aligned. - Placement also remains integer based for now. - Repainting is untouched as we always expand outwards (QRectF::toAdjustedRect(). - Decoration is untouched for now - Rules are integer in the config, but floating in the adjusting/API This should also be fine. At some point we'll add a method to snap to the device pixel grid. Effectively `round(value * dpr) / dpr` though right now things mostly work. This also gets rid of a lot of hacks for QRect right and bottom which are very confusing. Parts to watch out in the port are: QRectF::contains now includes edges QRectF::right and bottom are now sane so previous hacks have to be removed QRectF(QPoint, QPoint) behaves differently for the same reason QRectF::center too In test results some adjusted values which are the result of QRect.center because using QRectF's center should behave the same to the user.
184 lines
6.1 KiB
C++
184 lines
6.1 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 "deleted.h"
|
|
#include "inputmethod.h"
|
|
#include "output.h"
|
|
#include "platform.h"
|
|
#include "wayland/output_interface.h"
|
|
#include "wayland/seat_interface.h"
|
|
#include "wayland/surface_interface.h"
|
|
#include "wayland/textinput_v2_interface.h"
|
|
#include "wayland/textinput_v3_interface.h"
|
|
#include "wayland_server.h"
|
|
#include "workspace.h"
|
|
|
|
using namespace KWaylandServer;
|
|
|
|
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::mapped, this, &InputPanelV1Window::updateDepth);
|
|
|
|
connect(panelSurface, &InputPanelSurfaceV1Interface::topLevel, this, &InputPanelV1Window::showTopLevel);
|
|
connect(panelSurface, &InputPanelSurfaceV1Interface::overlayPanel, this, &InputPanelV1Window::showOverlayPanel);
|
|
connect(panelSurface, &InputPanelSurfaceV1Interface::destroyed, this, &InputPanelV1Window::destroyWindow);
|
|
|
|
InputMethod::self()->setPanel(this);
|
|
}
|
|
|
|
void InputPanelV1Window::showOverlayPanel()
|
|
{
|
|
setOutput(nullptr);
|
|
m_mode = Overlay;
|
|
reposition();
|
|
showClient();
|
|
setReadyForPainting();
|
|
}
|
|
|
|
void InputPanelV1Window::showTopLevel(OutputInterface *output, InputPanelSurfaceV1Interface::Position position)
|
|
{
|
|
Q_UNUSED(position);
|
|
m_mode = Toplevel;
|
|
setOutput(output);
|
|
showClient();
|
|
}
|
|
|
|
void InputPanelV1Window::allow()
|
|
{
|
|
setReadyForPainting();
|
|
reposition();
|
|
}
|
|
|
|
void KWin::InputPanelV1Window::reposition()
|
|
{
|
|
if (!readyForPainting()) {
|
|
return;
|
|
}
|
|
|
|
switch (m_mode) {
|
|
case Toplevel: {
|
|
QSizeF panelSize = surface()->size();
|
|
if (!panelSize.isValid() || panelSize.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
QRectF availableArea;
|
|
QRectF outputArea;
|
|
if (m_output) {
|
|
outputArea = m_output->geometry();
|
|
if (waylandServer()->isScreenLocked()) {
|
|
availableArea = outputArea;
|
|
} else {
|
|
availableArea = workspace()->clientArea(MaximizeArea, this, m_output);
|
|
}
|
|
} else {
|
|
availableArea = workspace()->clientArea(MaximizeArea, this);
|
|
outputArea = workspace()->clientArea(FullScreenArea, this);
|
|
}
|
|
|
|
panelSize = panelSize.boundedTo(availableArea.size());
|
|
QRectF geo(QPointF(availableArea.left(), availableArea.top() + availableArea.height() - panelSize.height()), panelSize);
|
|
geo.translate((availableArea.width() - panelSize.width()) / 2, availableArea.height() - outputArea.height());
|
|
moveResize(geo);
|
|
} break;
|
|
case Overlay: {
|
|
auto textInputSurface = waylandServer()->seat()->focusedTextInputSurface();
|
|
auto textWindow = waylandServer()->findWindow(textInputSurface);
|
|
QRect 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());
|
|
|
|
// Reuse the similar logic like xdg popup
|
|
QRectF popupRect(popupOffset(cursorRectangle, Qt::BottomEdge | Qt::LeftEdge, Qt::RightEdge | Qt::BottomEdge, surface()->size()), surface()->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, surface()->size()), surface()->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());
|
|
}
|
|
}
|
|
moveResize(popupRect);
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
void InputPanelV1Window::destroyWindow()
|
|
{
|
|
markAsZombie();
|
|
|
|
Deleted *deleted = Deleted::create(this);
|
|
Q_EMIT windowClosed(this, deleted);
|
|
StackingUpdatesBlocker blocker(workspace());
|
|
waylandServer()->removeWindow(this);
|
|
deleted->unrefWindow();
|
|
|
|
delete this;
|
|
}
|
|
|
|
NET::WindowType InputPanelV1Window::windowType(bool, int) const
|
|
{
|
|
return NET::Utility;
|
|
}
|
|
|
|
QRectF InputPanelV1Window::inputGeometry() const
|
|
{
|
|
return readyForPainting() ? QRectF(surface()->input().boundingRect()).translated(pos()) : QRectF();
|
|
}
|
|
|
|
void InputPanelV1Window::setOutput(OutputInterface *outputIface)
|
|
{
|
|
if (m_output) {
|
|
disconnect(m_output, &Output::geometryChanged, this, &InputPanelV1Window::reposition);
|
|
}
|
|
|
|
m_output = waylandServer()->findOutput(outputIface);
|
|
|
|
if (m_output) {
|
|
connect(m_output, &Output::geometryChanged, this, &InputPanelV1Window::reposition);
|
|
}
|
|
}
|
|
|
|
void InputPanelV1Window::moveResizeInternal(const QRectF &rect, MoveResizeMode mode)
|
|
{
|
|
Q_UNUSED(mode)
|
|
updateGeometry(rect);
|
|
}
|
|
|
|
} // namespace KWin
|