From 9d73e385153656ef998cc8f6378bb01ff36b2090 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 2 Nov 2016 19:24:07 +0100 Subject: [server] Rewrite positioning logic of connection frames This fixes sevceral bugs: * Frames moved into virtually expanded area (for keeping aspect ratio) could be out of bounds after a window resize before * Finding a free slot to place a frame was slightly sped up * Finding a free slot is not used when loading a room layout, as it was (still is) pretty sluggish for the user * Snap to grid worked incorrectly, did not pick closest grid position --- src/server/mainwindow/mainwindow.cpp | 162 +++++++++++++++++++---------------- 1 file changed, 89 insertions(+), 73 deletions(-) (limited to 'src/server/mainwindow/mainwindow.cpp') diff --git a/src/server/mainwindow/mainwindow.cpp b/src/server/mainwindow/mainwindow.cpp index cb2e4da..87ecca4 100644 --- a/src/server/mainwindow/mainwindow.cpp +++ b/src/server/mainwindow/mainwindow.cpp @@ -217,25 +217,23 @@ MainWindow::~MainWindow() delete ui; } -/** Euclidean distance (why is this not implemented in QPoint?) */ -float distance(QPoint a, QPoint b) +/** Squared euclidean distance (why is this not implemented in QPoint?) */ +int distance(QPoint a, QPoint b) { - int dx = a.x() - b.x(); - int dy = a.y() - b.y(); - int sum = dx * dx + dy * dy; - return sqrt((float) sum); + const int dx = a.x() - b.x(); + const int dy = a.y() - b.y(); + const int sum = dx * dx + dy * dy; + return sum; } -float distance(QPointF a, QPointF b) +int distance(QPointF a, QPointF b) { - int dx = a.x() - b.x(); - int dy = a.y() - b.y(); - int sum = dx * dx + dy * dy; - return sqrt(sum); + const int dx = a.x() - b.x(); + const int dy = a.y() - b.y(); + const int sum = dx * dx + dy * dy; + return sum; } - - /***************************************************************************//** * \brief: find the closest available frame. * @@ -251,6 +249,14 @@ float distance(QPointF a, QPointF b) */ QPoint MainWindow::closestFreeSlot(QPoint preferredPixels, ConnectionFrame* toIgnore) { + const bool pickFirstOne = ( preferredPixels == QPoint(-1, -1) ); + if (!pickFirstOne && toIgnore != NULL) { + // Check if we're already in the desired location and skip all the checks + QPoint desired = QPoint(preferredPixels.x() / getTileWidthPx(), preferredPixels.y() / getTileHeightPx()); + if (desired == toIgnore->getGridPosition()) { + return desired; + } + } const QSize& clientSize = serverApp->getCurrentRoom()->clientSize; bool grid[_tilesX][_tilesY]; memset(grid, 0, sizeof(bool) * _tilesX * _tilesY); /* set everything to false */ @@ -260,7 +266,7 @@ QPoint MainWindow::closestFreeSlot(QPoint preferredPixels, ConnectionFrame* toIg if (*it == toIgnore) { continue; } - const QPoint& p = (*it)->getGridPosition(); + const QPoint p = (*it)->getGridPosition(); for (int x = p.x(); x < p.x() + clientSize.width(); x++) { for (int y = p.y(); y < p.y() + clientSize.height(); y++) { @@ -283,18 +289,23 @@ QPoint MainWindow::closestFreeSlot(QPoint preferredPixels, ConnectionFrame* toIg } } } -endLoop: - if (isFree) { freePositions << QPoint(x, y); } +endLoop: ; + if (isFree) { + freePositions << QPoint(x, y); + if (pickFirstOne) { + goto endSearch; + } + } } } +endSearch: ; /* among all the free positions, find the closest */ - float min_distance = 10000; - QPoint bestPosition = QPoint(-1, -1); + float min_distance = 1000000; + QPoint bestPosition = QPoint(0, 0); for (QPoint freePos : freePositions) { - QPoint freePosCenter( freePos.x() * getTileWidthPx() + getTileWidthPx() * clientSize.width() / 2, - freePos.y() * getTileHeightPx() + getTileHeightPx() * clientSize.height() / 2); - float dist = distance(freePosCenter, preferredPixels); + const QPoint freePosPx( freePos.x() * getTileWidthPx(), freePos.y() * getTileHeightPx() ); + const float dist = distance(freePosPx, preferredPixels); if (dist < min_distance) { min_distance = dist; bestPosition = freePos; @@ -307,10 +318,7 @@ endLoop: void MainWindow::placeFrameInFreeSlot(ConnectionFrame* frame, QPoint preferredPixels) { QPoint bestPosition = closestFreeSlot(preferredPixels, frame); - frame->setGridPosition(bestPosition); - QPoint freePosPx(bestPosition.x() * getTileWidthPx(), bestPosition.y() * getTileHeightPx()); - frame->setCurrentPosition(freePosPx); } /***************************************************************************//** @@ -321,14 +329,12 @@ void MainWindow::placeFrameInFreeSlot(ConnectionFrame* frame, QPoint preferredPi */ ConnectionFrame* MainWindow::createFrame() { - // Allocate and resize - int width = getTileWidthPx() * serverApp->getCurrentRoom()->clientSize.width(); - int height = getTileHeightPx() * serverApp->getCurrentRoom()->clientSize.height(); - - ConnectionFrame *cf = new ConnectionFrame(ui->frmRoom, width, height); + ConnectionFrame *cf = new ConnectionFrame(this, ui->frmRoom); + // Move to any free tile + placeFrameInFreeSlot(cf); _clientFrames.append(cf); cf->show(); - connect(cf, SIGNAL(frameMoved(bool, ConnectionFrame *)), this, SLOT(onPlaceFrame(bool, ConnectionFrame *))); + connect(cf, SIGNAL(frameMoved(ConnectionFrame *)), this, SLOT(onPlaceFrame(ConnectionFrame *))); connect(cf, SIGNAL(clicked(ConnectionFrame *)), this, SLOT(onFrameClicked(ConnectionFrame *))); return cf; } @@ -339,20 +345,14 @@ ConnectionFrame* MainWindow::createFrame() * Also connect signals frameMoved() and clicked() with slots. * @return ConnectionFrame* */ -ConnectionFrame* MainWindow::createFrame(QString computerId, QPoint pxCoord, QPoint gridPosition) +ConnectionFrame* MainWindow::createFrame(QString computerId, QPoint gridPosition) { - // Allocate and resize - const Room* room = serverApp->getCurrentRoom(); - int width = getTileWidthPx() * (room == NULL ? 1 : room->clientSize.width()); - int height = getTileHeightPx() * (room == NULL ? 1 : room->clientSize.height()); - - ConnectionFrame *cf = new ConnectionFrame(ui->frmRoom, width, height); + ConnectionFrame *cf = new ConnectionFrame(this, ui->frmRoom); cf->setComputerId(computerId); - cf->setCurrentPosition(pxCoord); cf->setGridPosition(gridPosition); _clientFrames.append(cf); cf->show(); - connect(cf, SIGNAL(frameMoved(bool, ConnectionFrame *)), this, SLOT(onPlaceFrame(bool, ConnectionFrame *))); + connect(cf, SIGNAL(frameMoved(ConnectionFrame *)), this, SLOT(onPlaceFrame(ConnectionFrame *))); connect(cf, SIGNAL(clicked(ConnectionFrame *)), this, SLOT(onFrameClicked(ConnectionFrame *))); return cf; } @@ -480,41 +480,54 @@ AspectStatus checkAspectRatio(const QSize& frameSize, const QSize& gridSize) */ void MainWindow::resizeEvent(QResizeEvent* e) { - const Room* room = serverApp->getCurrentRoom(); - const QSize& clientSize = room->clientSize; - - if (ui->frmRoom->size().width() < 100 || ui->frmRoom->size().height() < 100 || _tilesX <= 0 || _tilesY <= 0) { return; } + if (ui->frmRoom->size().width() < 100 || ui->frmRoom->size().height() < 100) { + return; + } + const Room* room = serverApp->getCurrentRoom(); QSize newGridSize = room->gridSize; + const QSize& clientSize = room->clientSize; - /* do we have to add virtual columns? */ - while (checkAspectRatio(ui->frmRoom->size(), newGridSize) == GRID_TOO_WIDE) { - /* add row */ - newGridSize.setHeight(newGridSize.height() + 1); - } - while (checkAspectRatio(ui->frmRoom->size(), newGridSize) == GRID_TOO_TALL) { - /* add column */ - newGridSize.setWidth(newGridSize.width() + 1); + // Check if any frames have been moved beyond the room dimensions + for (auto it = _clientFrames.begin(); it != _clientFrames.end(); ++it) { + QPoint bounds = (*it)->getGridPosition(); + while (bounds.x() + clientSize.width() > newGridSize.width()) { + newGridSize += QSize(1, 0); + } + while (bounds.y() + clientSize.height() > newGridSize.height()) { + newGridSize += QSize(0, 1); + } } + + /* do we have to add virtual columns or rows? */ + AspectStatus status; + do { + status = checkAspectRatio(ui->frmRoom->size(), newGridSize); + if (status == GRID_TOO_WIDE) { + /* add row */ + newGridSize += QSize(0, 1); + } + if (status == GRID_TOO_TALL) { + /* add column */ + newGridSize += QSize(1, 0); + } + } while (status != GRID_OK); this->_tilesX = newGridSize.width(); this->_tilesY = newGridSize.height(); + const int maxX = _tilesX - clientSize.width(); + const int maxY = _tilesY - clientSize.height(); /* Bring back frames which are now out of the screen */ for (auto it = _clientFrames.begin(); it != _clientFrames.end(); ++it) { const QPoint gp = (*it)->getGridPosition(); - if ( gp.x() >= _tilesX || gp.y() >= _tilesY ) { - placeFrameInFreeSlot(*it, (*it)->getCurrentPosition()); + if ( gp.x() > maxX || gp.y() > maxY ) { + placeFrameInFreeSlot(*it, (*it)->pos()); } } /* Resize all connection windows */ for (auto it = _clientFrames.begin(); it != _clientFrames.end(); ++it) { - int newPosX = (*it)->getGridPosition().x() * getTileWidthPx(); - int newPosY = (*it)->getGridPosition().y() * getTileHeightPx(); - QPoint newPos(newPosX, newPosY); - - (*it)->setSize(getTileWidthPx() * clientSize.width(), getTileHeightPx() * clientSize.height()); - (*it)->move(newPos); + (*it)->updateGeometry(); } /* update background image label */ @@ -607,13 +620,13 @@ void MainWindow::reset() * Place frame to from user specified position. Should be called when a * connectionFrame is dropped during the "drag-n-drop". */ -void MainWindow::onPlaceFrame(bool activateTrash, ConnectionFrame* connectionFrame) +void MainWindow::onPlaceFrame(ConnectionFrame* connectionFrame) { // if (_tilesX <= 0 || _tilesY <= 0) return; - const QPoint &preferredPixels = connectionFrame->frameGeometry().center(); + const QPoint preferredPixels = connectionFrame->pos(); placeFrameInFreeSlot(connectionFrame, preferredPixels); - resizeEvent(NULL); + //resizeEvent(NULL); } /***************************************************************************//** @@ -737,11 +750,9 @@ void MainWindow::reloadCurrentRoom() for (auto it = room->clientPositions.begin(); it != room->clientPositions.end(); ++it) { QString computerId = it.key(); QPoint pos = it.value(); - QPoint pxPos(pos.x() * getTileWidthPx(), pos.y() * getTileHeightPx()); - ConnectionFrame *cf = createFrame(computerId, pxPos, pos); - cf->move(cf->getCurrentPosition()); - onPlaceFrame(false, cf); + ConnectionFrame *cf = createFrame(computerId, pos); + onPlaceFrame(cf); if (computerId == room->tutorIP) { qDebug() << "set computer with id " << computerId << " as tutor per configuration"; if (getTutorFrame() != NULL) { @@ -808,13 +819,24 @@ void MainWindow::onReloadRoomOk() int MainWindow::getTileWidthPx() const { + return ui->frmRoom->size().width() / _tilesX; } + int MainWindow::getTileHeightPx() const { return ui->frmRoom->size().height() / _tilesY; } +QRect MainWindow::calcFrameGeometry(ConnectionFrame* frame) const +{ + const QPoint pos = frame->getGridPosition(); + const QSize& clientSize = serverApp->getCurrentRoom()->clientSize; + const int w = getTileWidthPx(); + const int h = getTileHeightPx(); + return QRect(pos.x() * w, pos.y() * h, w * clientSize.width(), h * clientSize.height()); +} + /***************************************************************************//** * Display popup which explains possible actions about the buttons. */ @@ -1133,12 +1155,10 @@ void MainWindow::onClientAuthenticated(Client* client) // New one, create ConnectionFrame *cf = createFrame(); - // Move to any free tile - placeFrameInFreeSlot(cf); // Assign client instance cf->assignClient(client); - resizeEvent(NULL); // This is where all the positioning should be + //resizeEvent(NULL); // This is where all the positioning should be tellClientCurrentSituation(client); updateExamMode(); @@ -1276,10 +1296,6 @@ void MainWindow::onDeleteClient() lockContextButtons(); updateExamMode(); return; - } else { - frame->move(frame->getPreviousPosition()); - updateExamMode(); - return; } } -- cgit v1.2.3-55-g7522