Reduce immediate modeness of Window

This commit is contained in:
p
2026-10-03 02:48:04 +02:00
parent 3bde04502d
commit 28c851c385
9 changed files with 116 additions and 116 deletions
+2
View File
@@ -2022,6 +2022,8 @@ open_directory(Browser &b, const QUrl &url, bool record, float side_scroll)
push_hist(b.hist_back_, b);
}
b.dir_url_ = dir;
if (b.page_ && b.page_->host && b.page_->host->retitle)
b.page_->host->retitle();
b.size_cache_.clear();
invalidate_thumbs(b);
b.scroll_.offset = 0;
+4
View File
@@ -32,6 +32,10 @@ struct HostActions {
std::function<std::vector<std::string>()> bookmarks;
std::function<bool(const QUrl &url)> bookmarked;
std::function<void(QUrl url)> toggle_bookmark;
// The viewer has finished an open, with an image or with an error.
std::function<void()> opened;
// The page now shows another location, which the title has to follow.
std::function<void()> retitle;
};
// The menu that a right click on a file opens: what this application knows
+2
View File
@@ -83,6 +83,8 @@ void
Cropper::open(const QUrl &url)
{
this->jpeg_url_ = url;
if (this->page_ && this->page_->host && this->page_->host->retitle)
this->page_->host->retitle();
this->file_.clear();
this->image_.reset();
this->grid_ = {};
+4 -14
View File
@@ -776,7 +776,8 @@ apply_open(Viewer &v, uint64_t gen, const Viewer::CachedOpen &cached)
const string &message = cached.message;
v.cms_icc_ = cached.cms_icc;
v.opening_ = false;
v.open_done_ = true;
if (v.page_ && v.page_->host && v.page_->host->opened)
v.page_->host->opened();
v.set_message(message);
if (!message.empty())
qWarning("%s: %s", qUtf8Printable(v.url_.toString(QUrl::PrettyDecoded)),
@@ -1087,7 +1088,6 @@ start_open(Viewer &v, bool invalidate)
v.open_gen_++;
v.scale_gen_++;
v.opening_ = true;
v.open_done_ = false;
v.detached_ = false;
v.scale_job_pending_ = false;
v.scale_failed_ = false;
@@ -1996,7 +1996,8 @@ Viewer::open(const QUrl &url)
if (url == this->url_ && this->image_ && this->image_->width &&
this->image_->height) {
this->opening_ = false;
this->open_done_ = true;
if (this->page_ && this->page_->host && this->page_->host->opened)
this->page_->host->opened();
request_render(*this);
return;
}
@@ -2027,7 +2028,6 @@ Viewer::cancel_loads()
this->open_gen_++;
this->scale_gen_++;
this->opening_ = false;
this->open_done_ = false;
this->detached_ = true;
this->scale_job_pending_ = false;
this->scale_failed_ = false;
@@ -2068,16 +2068,6 @@ Viewer::has_view() const
return !this->opening_ && !this->url_.isEmpty();
}
bool
Viewer::consume_open_done()
{
if (!this->open_done_)
return false;
this->open_done_ = false;
return true;
}
void
Viewer::screen_changed(
const ScreenState &state, bool changed, bool force_reload)
-2
View File
@@ -72,7 +72,6 @@ struct Viewer : Widget {
CieDiagram *cie_ = nullptr;
Column *tags_ = nullptr;
bool opening_ = false;
bool open_done_ = false;
bool detached_ = false;
// The URL identifies what is on screen; the loader below works on the
@@ -143,7 +142,6 @@ struct Viewer : Widget {
// Shown over the image until dismissed; an empty one takes it down.
void set_message(const std::string &message);
[[nodiscard]] bool has_view() const;
[[nodiscard]] bool consume_open_done();
void screen_changed(
const ScreenState &state, bool changed, bool force_reload) override;
void update(Kit &kit) override;
+9 -22
View File
@@ -4319,20 +4319,7 @@ Kit::clip_pop()
this->list_.pop_clip();
}
bool
Kit::set_dpr(float dpr)
{
const float next = dpr > 0.f ? dpr : 1.f;
if (abs(next - this->dpr_) < 0.01f)
return false;
this->dpr_ = next;
if (this->inited_)
reset_fonts();
return true;
}
bool
void
Kit::reset_fonts()
{
QFont font = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
@@ -4350,11 +4337,8 @@ Kit::reset_fonts()
this->text_cache_.texts.clear();
this->font_epoch_++;
string error;
if (!this->text_backend_.reset(font, this->dpr_, &error)) {
if (!this->text_backend_.reset(font, this->dpr_, &error))
qWarning("font backend reset failed: %s", error.c_str());
return false;
}
return true;
}
int
@@ -5480,7 +5464,10 @@ Kit::relayout_popups()
bool
Kit::set_host(float width_pts, float height_pts, float dpr)
{
const bool changed = set_dpr(dpr);
const float next = dpr > 0.f ? dpr : 1.f;
const bool changed = abs(next - this->dpr_) >= 0.01f;
if (changed)
this->dpr_ = next;
this->host_w_ = px(width_pts);
this->host_h_ = px(height_pts);
return changed;
@@ -5492,13 +5479,12 @@ Kit::frame_ui(Page &ui)
if (!this->inited_)
return;
// An inactive page may have missed a change to the window chrome.
if (this->root_ != &ui)
ui.invalidate_arrange();
this->root_ = &ui;
ui.content->update(*this);
if (ui.toolbar)
ui.toolbar->sync_buttons();
if (ui.titlebar)
ui.titlebar->sync(*this);
this->text_frame_++;
ui.arrange(*this, {0, 0, this->host_w_, this->host_h_});
@@ -5508,6 +5494,7 @@ Kit::frame_ui(Page &ui)
if (this->notify)
this->notify(Change::State, nullptr);
this->hot_ = hit(this->mouse_x_, this->mouse_y_);
sync_cursor();
tooltip(this->hot_);
prepare_tooltip(*this);
paint();
+2 -2
View File
@@ -1102,9 +1102,9 @@ struct Kit {
bool gesture(float x, float y, float scale_factor, float angle_delta);
bool mouse_double_click(
float x, float y, Qt::MouseButton button, unsigned mods);
bool set_dpr(float dpr);
// Whether the ratio changed, to which the host answers with reset_fonts().
bool set_host(float width_pts, float height_pts, float dpr);
bool reset_fonts();
void reset_fonts();
void bake_colours(const ScreenState &state);
void draw_icon(int x, int y, int size, const char *name, Colour colour);
// Coverage copies scalar alpha; other bitmaps are sRGB premultiplied.
+91 -73
View File
@@ -158,6 +158,12 @@ Window::Window(App *app, QWindow *parent) : QWindow(parent), app_(app)
resize(kWindowWidth, kWindowHeight);
connect(this, &QWindow::screenChanged, this,
[this](QScreen *new_screen) { handle_screen_change(new_screen); });
// Not on WindowStateChange: a state we ask for is taken for granted
// before the window system confirms it, if it ever does.
connect(shell(), &QWindow::windowStateChanged, this, [this] {
sync_host();
request_render();
});
this->ui_wake_.setSingleShot(true);
connect(
&this->ui_wake_, &QTimer::timeout, this, [this] { request_render(); });
@@ -178,10 +184,12 @@ Window::Window(App *app, QWindow *parent) : QWindow(parent), app_(app)
};
this->kit_.start_move = [this] {
this->system_grab_ = shell()->startSystemMove();
sync_host();
request_render();
};
this->kit_.start_resize = [this](Qt::Edges edges) {
this->system_grab_ = shell()->startSystemResize(edges);
sync_host();
request_render();
};
this->kit_.start_drag = [this](QMimeData *mime, const QImage &icon) {
@@ -298,7 +306,16 @@ Window::initialize(const QUrl &url, BrowseSetup setup, Mode mode)
if (change == SettingsChange::Preferences) {
this->renderer_.dither_enabled =
!this->app_->settings.disable_dithering;
this->settings_apply_pending_ = true;
// Not inside the dialog's Save: libcolord's synchronous calls spin
// the main loop, which would reap the dialog from under it.
this->kit_.post([this] {
if (this->viewer_)
this->viewer_->loaders_ =
this->app_->settings.enabled_loaders;
if (this->renderer_ready_)
apply_screen_profile(screen(), true);
request_render();
});
}
if (this->browser_)
this->browser_->rescan();
@@ -329,6 +346,7 @@ Window::initialize(const QUrl &url, BrowseSetup setup, Mode mode)
this->browser_->thumb_size_ =
this->app_->settings.browser_thumbnail_size;
}
sync_host();
apply_screen_profile(screen(), false);
if (mode == Mode::Browse)
@@ -521,10 +539,19 @@ Window::bind_host()
return;
if (this->browser_)
this->browser_->hist_forward_.clear();
open_viewer(url);
// Before the open, which completes right away when it is cached.
this->awaiting_view_ = true;
open_viewer(url);
request_render();
};
this->host_.opened = [this] {
if (!this->awaiting_view_)
return;
this->awaiting_view_ = false;
set_mode(Mode::View);
request_render();
};
this->host_.retitle = [this] { sync_title(); };
this->host_.new_window = [this](QUrl url) {
if (url.isEmpty())
url = current_url();
@@ -716,7 +743,7 @@ Window::sync_title()
QWindow *w = shell();
if (w->title() != title) {
w->setTitle(title);
// This runs every frame, and only here has the name really changed.
// Callers sync whenever it may have changed, but only here has it.
accessible_renamed(this);
}
if (this != w && this->title() != title)
@@ -1067,21 +1094,37 @@ Window::accessibleRoot() const
#endif
}
// Everything the platform tells about the window that the kit draws from.
// It is cheap, so whatever may change any of it syncs all of it.
void
Window::sync_csd()
Window::sync_host()
{
this->kit_.sync_cursor();
if (this->kit_.cursor_ != this->cursor_applied_) {
this->cursor_applied_ = this->kit_.cursor_;
setCursor(this->cursor_applied_);
if (!this->renderer_ready_)
return;
const float dpr = host_dpr(*this);
this->kit_.dpi_ = host_dpi(*this);
if (this->kit_.set_host(float(width()), float(height()), dpr))
reset_fonts();
QWindow *sh = shell();
this->kit_.fullscreen_ = bool(sh->windowState() & Qt::WindowFullScreen);
this->kit_.maximized_ = bool(sh->windowState() & Qt::WindowMaximized);
this->kit_.csd_ = this->csd_ && !this->kit_.fullscreen_;
this->kit_.active_ = this->system_grab_ || sh->isActive() || isActive();
const bool shadow = this->kit_.csd_ && !this->kit_.maximized_;
if (this->kit_.csd_shadow_ != shadow) {
this->kit_.csd_shadow_ = shadow;
for (auto &page : this->pages_)
if (page)
page->invalidate_arrange();
}
const bool shadow = this->kit_.csd_shadow_;
QRegion mask;
if (shadow) {
// Input still has to reach the resize band, which lies in the shadow.
// setMask() speaks logical points, while the frame is in pixels:
// one conversion back, rather than two roundings that disagree.
const float dpr = host_dpr(*this);
const auto pt = [dpr](int v) { return int(lround(double(v) / dpr)); };
const int band = int(lround(double(kResizeBorderPts)));
const Rect f = this->kit_.frame();
@@ -1089,12 +1132,25 @@ Window::sync_csd()
mask = QRegion(frame.adjusted(-band, -band, band, band));
}
setMask(mask);
if (QWindow *sh = shell(); sh != this)
if (sh != this)
sh->setMask(QRegion());
const float dpr = host_dpr(*this);
const uint32_t inset =
this->renderer_.dest_inset =
shadow ? uint32_t(max(0L, lround(double(kGlowPts) * double(dpr)))) : 0;
this->renderer_.dest_inset = inset;
}
// Text shaped and measured with the old fonts is stale, and so is whatever
// the contents sized after them.
void
Window::reset_fonts()
{
if (!this->renderer_ready_)
return;
this->kit_.reset_fonts();
for (auto &page : this->pages_)
if (page && page->content)
page->content->rescale(this->kit_);
request_render();
}
void
@@ -1155,6 +1211,7 @@ Window::open_viewer(const QUrl &url)
return;
this->viewer_->open(url);
sync_viewer_preloads();
sync_title();
}
void
@@ -1216,30 +1273,6 @@ Window::render()
this->renderer_.resize(pixel_size());
this->resize_pending_ = false;
}
if (this->settings_apply_pending_ && this->renderer_.extent().width &&
this->renderer_.extent().height) {
if (this->viewer_)
this->viewer_->loaders_ = this->app_->settings.enabled_loaders;
apply_screen_profile(screen(), true);
this->settings_apply_pending_ = false;
}
const float w = float(width());
const float h = float(height());
const float dpr = host_dpr(*this);
this->kit_.dpi_ = host_dpi(*this);
const bool fullscreen = bool(shell()->windowState() & Qt::WindowFullScreen);
bool rescale = this->kit_.set_host(w, h, dpr);
if (this->font_change_pending_) {
this->font_change_pending_ = false;
if (!rescale)
rescale = this->kit_.reset_fonts();
} else if (this->kit_.text_backend_.settings_changed())
rescale = this->kit_.reset_fonts() || rescale;
if (rescale)
for (auto &page : this->pages_)
if (page && page->content)
page->content->rescale(this->kit_);
// Nothing to do for a resize: relayout_popups() re-places every popup
// and drops the ones whose opener stopped being shown.
@@ -1247,34 +1280,16 @@ Window::render()
if (!ui)
return;
// Completing an open below may change the mode after this page is drawn.
const bool show_image =
this->mode_ == Mode::View || this->mode_ == Mode::CropJpeg;
this->kit_.fullscreen_ = fullscreen;
this->kit_.maximized_ = bool(shell()->windowState() & Qt::WindowMaximized);
this->kit_.csd_ = this->csd_ && !fullscreen;
const bool shadow = this->kit_.csd_ && !this->kit_.maximized_;
if (this->kit_.csd_shadow_ != shadow) {
this->kit_.csd_shadow_ = shadow;
ui->invalidate_arrange();
}
this->kit_.active_ =
this->system_grab_ || shell()->isActive() || isActive();
sync_title();
if (ui->titlebar)
ui->titlebar->sync(this->kit_);
if (ui->toolbar)
ui->toolbar->busy = this->awaiting_view_ || ui->content->busy();
this->kit_.frame_ui(*ui);
sync_csd();
if (this->viewer_ && this->viewer_->consume_open_done() &&
this->awaiting_view_) {
this->awaiting_view_ = false;
set_mode(Mode::View);
request_render();
if (this->kit_.cursor_ != this->cursor_applied_) {
this->cursor_applied_ = this->kit_.cursor_;
setCursor(this->cursor_applied_);
}
const bool show_image =
this->mode_ == Mode::View || this->mode_ == Mode::CropJpeg;
const bool deferred = this->present_retry_.isActive();
const bool presented = !deferred &&
this->renderer_.draw_frame(this->kit_.list_.mesh(), show_image);
@@ -1356,6 +1371,7 @@ Window::handle_screen_change(QScreen *target_screen)
this->resize_pending_ = true;
if (this->renderer_ready_)
apply_screen_profile(target_screen, false);
sync_host();
request_render();
}
@@ -1454,12 +1470,12 @@ Window::event(QEvent *event)
}
case QEvent::DevicePixelRatioChange:
this->resize_pending_ = true;
sync_host();
request_render();
break;
case QEvent::ApplicationFontChange:
case QEvent::FontChange:
this->font_change_pending_ = true;
request_render();
case QEvent::ThemeChange:
// The only word of a new system font, which Qt has already re-read.
reset_fonts();
break;
case QEvent::PlatformSurface: {
auto *surface_event = (QPlatformSurfaceEvent *) event;
@@ -1526,6 +1542,10 @@ Window::focus_gained()
{
sync_macos_app_menu(this->app_);
accessible_activated(this);
sync_host();
// Nothing tells of font settings changing while we are in front.
if (this->renderer_ready_ && this->kit_.text_backend_.settings_changed())
reset_fonts();
request_render();
}
@@ -1546,18 +1566,16 @@ Window::focus_lost()
// Past the guard above, so that the hand-off it describes stays what it
// is: one window, and nothing for a screen reader to read out twice.
accessible_activated(this);
sync_host();
request_render();
}
bool
Window::eventFilter(QObject *watched, QEvent *event)
{
if ((watched == qGuiApp || watched == this || watched == shell()) &&
(event->type() == QEvent::ApplicationFontChange ||
event->type() == QEvent::FontChange)) {
this->font_change_pending_ = true;
request_render();
}
// Qt then posts a copy to every top-level window, which says nothing new.
if (watched == qGuiApp && event->type() == QEvent::ApplicationFontChange)
reset_fonts();
if (watched != this && watched != shell())
return false;
@@ -1568,6 +1586,7 @@ Window::eventFilter(QObject *watched, QEvent *event)
(event->type() == QEvent::Enter || event->type() == QEvent::MouseMove ||
event->type() == QEvent::MouseButtonPress)) {
this->system_grab_ = false;
sync_host();
request_render();
}
if (watched != this)
@@ -1619,6 +1638,7 @@ void
Window::resizeEvent(QResizeEvent *)
{
this->resize_pending_ = true;
sync_host();
request_render();
}
@@ -1662,7 +1682,6 @@ Window::open_any(const QUrl &input)
(url_to_path(url).isEmpty() || QFileInfo(url_to_path(url)).isDir()))
return;
this->cropper_->open(url);
sync_title();
request_render();
return;
}
@@ -1719,7 +1738,6 @@ Window::reveal_file(const QUrl &input)
cancel_viewer_loads();
set_mode(Mode::Browse);
request_render();
sync_title();
}
void
+2 -3
View File
@@ -77,7 +77,8 @@ class Window final : public QWindow
void bind_host();
void set_mode(Mode m);
void sync_title();
void sync_csd();
void sync_host();
void reset_fonts();
bool handle_native_gesture(QNativeGestureEvent *event);
bool handle_touch(QTouchEvent *event);
bool handle_input_method(QInputMethodEvent *event);
@@ -102,8 +103,6 @@ class Window final : public QWindow
bool renderer_ready_ = false;
bool exposed_ = false;
bool resize_pending_ = false;
bool settings_apply_pending_ = false;
bool font_change_pending_ = false;
bool update_pending_ = false;
std::string screen_profile_label_; // Its name and source
// What the screen profile last saw of Windows Advanced Color, against