Skip to content

Commit

Permalink
Fix reports being shown out of screen
Browse files Browse the repository at this point in the history
When connecting to a game and receiving an historian report, they would
often be shown out-of-screen. This is ultimately caused by the resizing
of the client window happening only after the game state is fully
loaded. The placement algorithm has to provide sensible results when the
widget doesn't fit inside the (small) base window size.

The complicated placement algorithm relied on an initial guess for the
position and could result in the widget being shown off-screen when the
window was too small. Replace it with a linear scan going left-to-right
and top-to-bottom across the entire screen. Also avoid recursion,
permitting more attempts by lifting the limitation due to the stack
size.

This doesn't entirely solve the issue of widgets being shown in an
unexpected location when connecting to a game: when the algorithm finds
no solution, it now defaults to 0,0 to maximize visible content. This
can in particular be the case for the small window size used right
before connecting. I think a full revision of the pregame screens is
needed, but this is way beyond the scope of this PR and won't make it to
3.1.

Closes #1989.
  • Loading branch information
lmoureaux authored and jwrober committed Oct 28, 2024
1 parent fe77b02 commit 74285da
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 111 deletions.
1 change: 1 addition & 0 deletions client/dialogs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,7 @@ void popup_notify_dialog(const char *caption, const char *headline,

freeciv::report_widget *nd = new freeciv::report_widget(
caption, headline, lines, queen()->mapview_wdg);
nd->move(queen()->mapview_wdg->find_place(nd->size()));
nd->show();
}

Expand Down
128 changes: 29 additions & 99 deletions client/views/view_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,6 @@ extern int last_center_capital;
extern int last_center_player_city;
extern int last_center_enemy_city;

/**
Check if point x, y is in area (px -> pxe, py - pye)
*/
bool is_point_in_area(int x, int y, int px, int py, int pxe, int pye)
{
return x >= px && y >= py && x <= pxe && y <= pye;
}

/**
Draws calculated trade routes
*/
Expand Down Expand Up @@ -400,105 +392,43 @@ void map_view::wheelEvent(QWheelEvent *event)
}

/**
Sets new point for new search
* Finds a suitable location for a widget of the given size, avoiding overlap
* with other widgets.
*/
void map_view::resume_searching(int pos_x, int pos_y, int &w, int &h,
int wdth, int hght, int recursive_nr,
bool direction)
QPoint map_view::find_place(const QSize &size) const
{
int new_pos_x, new_pos_y;
// We scan all possible locations from left to right and from top to
// bottom. At each step we check for overlap with other widgets. We return
// the first point without overlap.
const auto step = 5;

recursive_nr++;
new_pos_x = pos_x;
new_pos_y = pos_y;
// This is the rectangle that would be covered by the widget.
auto candidate = QRect(QPoint(), size);

if (direction) {
if (pos_y + hght + 4 < height() && pos_x < width() / 2) {
new_pos_y = pos_y + 5;
} else if (pos_x > 0 && pos_y > 10) {
new_pos_x = pos_x - 5;
} else if (pos_y > 0) {
new_pos_y = pos_y - 5;
} else if (pos_x + wdth + 4 < this->width()) {
new_pos_x = pos_x + 5;
}
} else {
if (pos_y + hght + 4 < height() && pos_x > width() / 2) {
new_pos_y = pos_y + 5;
} else if (pos_x > 0 && pos_y > 10) {
new_pos_x = pos_x - 5;
} else if (pos_y > 0) {
new_pos_y = pos_y - 5;
} else if (pos_x + wdth + 4 < this->width()) {
new_pos_x = pos_x + 5;
const auto children = findChildren<QWidget *>();
while (candidate.bottom() < height()) {
// Check if the candidate rectangle intersects with another widget
if (!std::any_of(children.begin(), children.end(),
[candidate](const QWidget *child) {
return child->isVisible()
&& candidate.intersects(child->rect());
})) {
// We found a good location
return candidate.topLeft();
}
}

find_place(new_pos_x, new_pos_y, w, h, wdth, hght, recursive_nr,
direction);
}

/**
Searches place for widget with size w and height h
Starts looking from position pos_x, pos_y, going clockwork
Returns position as (w,h)
Along with resume_searching its recursive function.
direction = false (default) = clockwise
*/
void map_view::find_place(int pos_x, int pos_y, int &w, int &h, int wdth,
int hght, int recursive_nr, bool direction)
{
int i;
int x, y, xe, ye;
QList<fcwidget *> widgets = this->findChildren<fcwidget *>();
bool cont_searching = false;

if (recursive_nr >= 1000) {
/**
* give up searching position
*/
return;
}
/**
* try position pos_x, pos_y,
* check middle and borders if aren't above other widget
*/

for (i = 0; i < widgets.count(); i++) {
if (!widgets[i]->isVisible()) {
continue;
}
x = widgets[i]->pos().x();
y = widgets[i]->pos().y();

if (x == 0 && y == 0) {
continue;
}
xe = widgets[i]->pos().x() + widgets[i]->width();
ye = widgets[i]->pos().y() + widgets[i]->height();

if (is_point_in_area(pos_x, pos_y, x, y, xe, ye)) {
cont_searching = true;
}
if (is_point_in_area(pos_x + wdth, pos_y, x, y, xe, ye)) {
cont_searching = true;
}
if (is_point_in_area(pos_x + wdth, pos_y + hght, x, y, xe, ye)) {
cont_searching = true;
}
if (is_point_in_area(pos_x, pos_y + hght, x, y, xe, ye)) {
cont_searching = true;
}
if (is_point_in_area(pos_x + wdth / 2, pos_y + hght / 2, x, y, xe, ye)) {
cont_searching = true;
// Move the candidate rectangle to the next point
if (candidate.right() + step < width()) {
// Step right
candidate.moveTo(candidate.left() + step, candidate.top());
} else {
// Next line
candidate.moveTo(0, candidate.top() + step);
}
}
w = pos_x;
h = pos_y;
if (cont_searching) {
resume_searching(pos_x, pos_y, w, h, wdth, hght, recursive_nr,
direction);
}

// We didn't find any solution :'(
return QPoint();
}

/**
Expand Down
8 changes: 4 additions & 4 deletions client/views/view_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,9 @@ class map_view : public QWidget {

public:
map_view();
void find_place(int pos_x, int pos_y, int &w, int &h, int wdth, int hght,
int recursive_nr, bool direction = false);
void resume_searching(int pos_x, int pos_y, int &w, int &h, int wdtht,
int hght, int recursive_nr, bool direction);

QPoint find_place(const QSize &size) const;

void update_cursor(enum cursor_type);

void hide_all_fcwidgets();
Expand Down Expand Up @@ -105,6 +104,7 @@ private slots:
double m_scale = 1;
std::unique_ptr<QPropertyAnimation> m_origin_animation;
std::unique_ptr<QPropertyAnimation> m_scale_animation;

QPointer<freeciv::tileset_debugger> m_debugger = nullptr;
std::vector<QPointer<fcwidget>> m_hidden_fcwidgets;
};
Expand Down
8 changes: 0 additions & 8 deletions client/widgets/report_widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@
#include "widgets/report_widget.h"

// client
#include "dialogs_g.h"
#include "fc_client.h"
#include "fonts.h"
#include "page_game.h"
#include "views/view_map.h"

// Qt
Expand Down Expand Up @@ -55,12 +53,6 @@ report_widget::report_widget(const QString &caption, const QString &headline,
auto cw = new close_widget(this);
cw->setFixedSize(12, 12);
cw->put_to_corner();

auto x = width();
auto y = height();
queen()->mapview_wdg->find_place(queen()->mapview_wdg->width() - x - 4, 4,
x, y, x, y, 0);
move(x, y);
}

/**
Expand Down

0 comments on commit 74285da

Please sign in to comment.