Menu

#123 Manga mode confused by widgets

MComix_2.0.0
open
nobody
None
5
2024-02-07
2022-03-03
Ark
No

There is a bug regarding the initial scrollbar position when switching from one book (or double page?) to another.

  • Use manga mode and double page mode (combined).
  • The thumbnailer should be visible to enhance the undesired effect (see below).
  • The relative dimensions of everything should be such that both the vertical and the horizontal scrollbar appear for the main area and are enabled.
  • Maybe necessary: The width of the pages in the next book should be less then the width of the pages in the current book. (Also, at least in my case, for each book, all pages are of the same size but different from one book to the other).
  • Now, go from the current book to the next book. The main area should display the top-right corner of the right page, but instead it shows you some part closer to top-center.

I already tracked down the issue to these lines:

  • Stack traces show that it always happens this way: main.py:_draw_imagemain.py:scroll_to_predefinedmain.py:update_viewport_position.
  • In said MainWindow.update_viewport_position, there is a call self._hadjust.set_value(viewport_position[0]).
  • At that point, the value of viewport_position[0] is correct and less than self._hadjust.get_upper() so there should be no problem.
  • However, immediately after said call, self._hadjust.get_value() is different from viewport_position[0]. According to my (very few) tests, the difference matches exactly the sum of the widths of all widgets to the left and the right of the main area. Thus, for example, if the thumbnailer has a width of 100 and the vertical scrollbar of the main area has a width of 12, self._hadjust.get_value() will report a value that is 112 less than desired. The position of what is displayed in the main area to the user seems to be off, indicating that this bad value has been applied as such.
  • However, the horizontal scrollbar itself, as displayed to the user, is exactly how it should be. EDIT It is not. If you try to drag the content in the main area around, the horizontal scrollbar changes a bit to indicate that you can actually scroll further to the right. /EDIT That is, it tells you that you are at the rightmost position, despite the main area showing you parts of the double page that are closer to the center.
  • If you switch to the next double page in the same book (i.e. same width), the position will be correct and self._hadjust.get_value() will return the correct (desired) value, despite none of the other values changing.

This behavior seems to masquerade as normal in western mode, probably because the desired initial value for the horizontal scrollbar in this case is 0 so scrolling is trivial.

This bug might be related to GTK 3. I tried to fix this by moving the line self._scroll[0].queue_resize_no_redraw() just before self._hadjust.set_value(viewport_position[0]) but to no avail.

Any ideas?

Related

Feature Requests: #123
Feature Requests: #124
Feature Requests: #134

Discussion

  • Ark

    Ark - 2022-03-03
    • Description has changed:

    Diff:

    --- old
    +++ new
    @@ -12,7 +12,7 @@
    
     - In said `MainWindow.update_viewport_position`, there is a call `self._hadjust.set_value(viewport_position[0])`.
     - At that point, the value of `viewport_position[0]` is *correct* and less than `self._hadjust.get_upper()` so there should be no problem.
     - However, immediately after said call, `self._hadjust.get_value()` is different from `viewport_position[0]`. According to my (very few) tests, the difference matches exactly the sum of the widths of all widgets to the left and the right of the main area. Thus, for example, if the thumbnailer has a width of `100` and the vertical scrollbar of the main area has a width of `12`, `self._hadjust.get_value()` will report a value that is `112` less than desired. The position of what is displayed in the main area to the user seems to be off, indicating that this bad value has been applied as such.
    -- However, the horizontal scrollbar itself, as displayed to the user, is exactly how it should be. That is, it tells you that you are at the rightmost position, despite the main area showing you parts of the double page that are closer to the center.
    +- However, the horizontal scrollbar itself, as displayed to the user, is exactly how it should be. **EDIT** It is not. If you try to drag the content in the main area around, the horizontal scrollbar changes a bit to indicate that you can actually scroll further to the right. **/EDIT** That is, it tells you that you are at the rightmost position, despite the main area showing you parts of the double page that are closer to the center.
     - If you switch to the next double page in the same book (i.e. same width), the position will be correct and `self._hadjust.get_value()` will return the correct (desired) value, despite none of the other values changing.
    
     This behavior seems to masquerade as normal in western mode, probably because the desired initial value for the horizontal scrollbar in this case is 0 so scrolling is trivial.
    
     
  • Ark

    Ark - 2022-03-04

    I managed to produce a simple test case. You can use the attached file and the following (sketchy) shell script:

    tar xf bugdemo-doublepage-mangamode.tar.gz
    git apply bugdemo-doublepage-mangamode.patch
    mcomix doublepage1
    

    Then,

    1. Enable double page mode.
    2. Enable manga mode.
    3. Show thumbnailer.
    4. Reduce window size and/or zoom into the images until both scrollbars are visible.
    5. Go to the next directory (which should be doublepage2). Note how the first (white) page is not centered. (You may press CursorRight to confirm.)
    6. Got to the next double page (by pressing PageDown or similar). Note how the third (white) page is centered.
    7. Watch and compare the generated stderr output. When you do step 5, the wrong value is applied, but in step 6, the correct value is applied.

    Apparently, all pages can be of the same size, and the issue is observable when switching directories but not when switching pages within a directory.

     
  • FeRD

    FeRD - 2023-05-06

    Hmm. OK, first principles: I think I need more information about your exact setup, because there are a lot of moving parts here and they're all interrelated. I wasn't able to reproduce your exact issue, at least not so far. (Working from the original description; I haven't looked at your followup repro yet.)

    But I did encounter enough weirdness that makes me doubt this is a GTK bug, and feel it's very likely a bug in the MComix scroll/layout implementation. (Which is basically all custom — the event handling for the scroll, drag, and keypress events is custom, the scroll positioning is custom — hence the _hadjust and _vadjust — the layout of the images in the viewport is custom.) So, given that it's all a custom job, sort of by definition there can't be a Gtk bug... MComix is using Gtk in not-strictly-supported ways, so it gets to own all the bugs in how it does so.

    Only if we could prove that the behavior we're seeing is also exhibited by Gtk even when it's used in the standard/recommended fashion, could it then be bumped up to the Gtk devs as a bug on their end. (Which they'd be unlikely to fix anyway, because Gtk3 is a development cul-de-sac and has no resources devoted to maintaining it anymore. Unless the bug's also present in Gtk4, the response would be, "Upgrade and it'll go away." Which is... not unfair, from the perspective of their need to prioritize finite resources.)

    Anyway, here are my attempts and what I experienced. But first, all the settings that might be relevant here. For my initial attempt, my Preferences were set to:

    • In Behavior->Scroll, I had the first four items checked. For the distances I have 50, 50, 50, 3.
    • Behavior->Double page mode:
      • Flip two pages: ON
      • Show only one page: Always
      • Page auto-resizing: Prefer same size
      • Space between two: 2 pixels

    Now, I don't have any manga, don't expect to read any manga, and have probably never enabled manga mode before today. So I just loaded a traditional western comic archive from a directory full of them and switched on Manga mode for it, because it's not like MComix can tell the difference. Two-Page mode was already enabled so I left it enabled. Zoom I set to Fit-to-size, as that left me with page images big enough to exceed the available viewport even when MComix is maximized. The thumbnails are visible, as they always are for me — I keep them open and just let MComix auto-hide them while fullscreened, if I want undistracted reading. I didn't enter fullscreen mode for any of this testing, only maximized the window (which kept the thumbnails visible).

    Attempt 1

    First thing I realized, very quickly, was that I was going to have to turn off "Show only one page..." mode to even attempt repro'ing your issue. So I set that to Never.

    Attempt 2

    Second thing I realized was that "Smart scroll" would also have to go. With Smart Scroll enabled, the scroll positioning always appeared to be... well, not "correct" but "intentional", except HOLY WOW this cannot be how it's supposed to work!!??

    1. Scrolling down would scroll horizontally instead of vertically (seemingly by design); not necessarily a problem, except it was because...
    2. it turned the scroll advancement into something that felt almost like a typewriter platen. As I'd spin the mousewheel down, the view would start with the right side at the top-right edge of the right page, and track leftward until the left side made it to the top-left edge of the right page. Then it would jump back to the right edge, while shifting down... ... ... I'm going to generously estimate that the vertical shift was ~ 1/16 of the total page height. Probably less.

    So, just to scroll through a SINGLE page, with "smart" scroll enabled, I would have to make at least SIXTEEN right-to-left passes, while bumping incrementally downwards by a tiny bit on each pass. After I'd completed my 16–20 horizontal sweeps, I'd finally reach the bottom left edge of the right page, and be whisked off to the top-right edge of the left page. Lather, rinse, repeat. This is the least-smart "smart" scrolling I have ever encountered. Maybe tweaking some of those distance numbers in Preferences->Behavior->Scroll would make it suck less, but I didn't feel like wasting time experimenting. So, I turned that off too.

    Attempt 3

    So, now we're cooking. Scrolling vertically scrolls the page vertically, PgDn / PgUp move to the top-right or bottom-left of the next/previous dual-page spread, respectively, as does overscrolling while at the top or bottom edge of a page-spread. When viewing the last two-page spread of a book, overscrolling past the bottom advances to the next archive in the directory. All is right with the MComix world.

    In fact, a little too right. Because I mostly seem to be getting positioned correctly with each page switch. Mostly.

    The first little bit of weirdness I notice is that, whenever I scroll across file boundaries, as I start scrolling down the first spread of the newly-loaded file the horizontal scrollbar jump just sliiiightly to the left. Not even remotely to the same degree you reported, but there's unmistakably this tiny jump away from the right edge of the page. Happens every time, between every pair of files, and only on the first page(s) of a new file. Seems to be slightly more exaggerated when I have the window maximized, less so when it's smaller. So the distance seems to have something to do with the viewport dimensions relative to the image dimensions.

    It could be that you're experiencing a much more exaggerated version of that same phenomenon. I can think of plenty of possible reasons why it might be worse for you, though they're all only guesses:

    • Maybe your screen is significantly-higher, or -lower, resolution than my own 1920×1200 desktop display.
    • Maybe the comics pages you're viewing are significantly larger, or smaller, than the 1988×3056-pixel page images in the files I'm testing with. (I don't have disparate-page-sized archives handy, so I haven't introduced that variable yet.)
    • Maybe your scroll inputs are significantly higher- or lower-resolution than the ones from my optical mouse with physical scroll wheel.

    That last one bears some further scrutiny, too: How are you navigating, in MComix? Are you using a touchscreen, a trackpad, a mouse? If you're using a trackpad, does it use two-finger drags to scroll, and does it have a kinetic scrolling feature? A trackpad sending high-resolution scroll gestures with kinetic acceleration would produce exponentially more scroll events than my mouse wheel is even capable of generating, so it's possible that the MComix scroll event handler is being overwhelmed and mishandling scroll inputs far more severely on your device than on mine.

    One other observation I made in my testing, that has me leaning towards that theory: I've noticed that, when I scroll back across archives (scroll upwards from the first page of one file, to switch to the previous file in the directory), very often I don't end up at the bottom edge of the last page (spread) of the previous archive. Quite frequently, but not entirely consistently/predictably, I'll warp off of the first page(s) of one archive, and end up getting delivered four, six, eight pages before the last pair, in the previous archive.

    What appears to be happening is that, while the new (previous) archive is being loaded and there's no content in the MComix window, any leftover scroll inputs queued up — or any I actively make during the transition — are processed while the previous archive is in the process of being loaded. Because there are no pages displayed in the window, the scroll distance to switch pages is effectively 0 pixels, so my scroll-up events quickly jump backwards through the file until they land on a page spread that can be loaded and displayed, which then absorbs the scroll events. That, too, could be related to the issue you're seeing. But I need to bang on the scroll event handler code some more, before I can confirm/understand what's going on there.

     

    Last edit: FeRD 2023-05-06
  • FeRD

    FeRD - 2023-05-06

    Having read that, I wonder whether we need a ScrolledWindow

    The answer to that is probably, "Maybe but...".

    Yes, the GNOME HIG-conforming way to do this stuff would be to build the interface in Gtk4, with a GtkScrolledWindow as its primary display surface and event controllers and gestures handling the input events, in place of the current custom input event processing. (Direct access to input events is deprecated in Gtk3 and no longer available in Gtk4, so the current custom event handler code is unusable for Gtk4.)

    But building things that way would likely require sacrificing a fair number of MComix features that GtkScrolledWindow doesn't support. Even the GtkScrolledWindow docs themselves are quick to say:

    If a GtkScrolledWindow doesn’t behave quite as you would like, or doesn’t have exactly the right layout, it’s very possible to set up your own scrolling with GtkScrollbar and for example a GtkGrid.

    (And the strong subtext there is, "Don't come to us with feature requests to support your weird stuff natively in GtkScrolledWindow. We're not gonna add it, just build your own.)

    In order to have an interface that supports a sufficient number of the navigation features its custom event-handling code currently provides, MComix would almost certainly find itself in that exact boat pretty quickly. The only difference is, it would need custom Event Controller and GtkScrollable interface implementations, to replace the current custom event handler and image-painting code.

     

    Last edit: FeRD 2023-05-06
  • Ark

    Ark - 2023-05-07

    Thanks for butting in. This is the correct thread. :-)

    But I did encounter enough weirdness that makes me doubt this is a GTK bug, and feel it's very likely a bug in the MComix scroll/layout implementation.

    When I wrote that "This bug might be related to GTK 3", I merely wanted to express the idea that, in some ways, the bug might have something to do with GTK 3 or how MComix interacts with GTK 3, and that the bug is probably not due to faulty layout calculations on our side alone.

    MComix is using Gtk in not-strictly-supported ways, so it gets to own all the bugs in how it does so.

    That is probably correct.

    […] I'd finally reach the bottom left edge of the right page, and be whisked off to the top-right edge of the left page.

    Which is good because this is how you read a manga. The way you have to interact with a touch device to accomplish this, however, is certainly an issue. MComix should become more touch device friendly. This is not related to the bug we are discussing here, though.

    This is the least-smart "smart" scrolling I have ever encountered. Maybe tweaking some of those distance numbers in Preferences->Behavior->Scroll would make it suck less, but I didn't feel like wasting time experimenting.

    We certainly need different units for this feature, since absolute pixel distances are difficult to use here. However, this is an unrelated issue.

    Not even remotely to the same degree you reported, but there's unmistakably this tiny jump away from the right edge of the page.

    I think this is what I wanted you to observe. See below for more on it.

    How are you navigating, in MComix? Are you using a touchscreen, a trackpad, a mouse?

    I use the space bar most of the time. Combined with "Stretch small images" enabled, I mostly only need "Fit to size" (S key) for most pages and "Fit to both" (B key) for the rest. (You can tweak some details in the preferences.) This is three keys on a keyboard doing wonders (with the occasional shift+space bar in case I need to go back).

    […] so my scroll-up events quickly jump backwards through the file until they land on a page spread that can be loaded and displayed, which then absorbs the scroll events.

    Your explanation for this behavior seems to match mine here.

    That, too, could be related to the issue you're seeing.

    Maybe, but I can trigger this behavior using a single key press (PgUp or PgDown). If fixing one of the issues happens to implicitly also fix the other, I would not be too surprised, though.

    That being said, I just found an easier way to demonstrate what the issue at hand looks like. Read on in the next post!

     
  • Ark

    Ark - 2023-05-07

    First of all, what would the ideal way look like, given an imaginary Ideal Toolkit™?

    Let's say no book is opened, so no widgets except menu bar and status bar. Now, we open a book. First, we add the thumbnailer and make it visible. It might start adding and displaying thumbnails by itself asynchronously, but it must start with no selected thumbnail because what we see in the main area, which is empty right now, does not match any of the thumbnails. Concurrently, the first image is loaded sufficiently enough by now to know its dimensions (including rotation etc.), so we can do layout calculations. First, we freeze the viewport so it will not change its content while we are busy calculating. We start with no scroll bars, so we turn them off (but this is not visible because the entire viewport is frozen) so we know how big the viewport is at most. Then, we might need to zoom into or out of the image, depending on settings and viewport space available. We might conclude that we need to add a scroll bar, so we ask Ideal Toolkit™ to do just that (invisibly to the user, of course). As a result, we might have less actual viewport space available, so we need to redo our calculations. Again, we might conclude that we need to add yet another scroll bar, and so on, so we re-iterate until we have added all the scroll bars that are both available and needed. Now, all calculations have converged to their respective final values, so we can put the results to good use. We inform Ideal Toolkit™ about the dimensions of the scrollable area and where images should appear (and how big they are). Then, we scroll to the correct corner (e.g. top right when in manga mode), mark the appropriate thumbnail in the thumbnailer as selected, and thaw up everything. Later, we repaint the content of the viewport as soon as a new image is fully loaded and prepared for display.

    Kind of like this. Now, back to reality.

    I am not sure what is going on here, but my attempt to understand this mess is that the most interesting function is mcomix.MainWindow._draw_image which is queued to be called somehow from outside using some GObject.idle_add magic implemented in draw_image (note the absence of a leading _). If this really happens as asynchronously as I think it does, _waiting_for_redraw should be turned into something that guarantees atomic operations, I guess. Or maybe we do not redraw often enough? This is so confusing! Anyway, back to _draw_image. Somewhere in there, where all the layout computations are performed, _show_scrollbars is called in the hopes of forcing GTK 3 to recalculate the dimensions of the viewport synchronously so the calculations can go on with the new values immediately.

    And this is where, I guess, we run into this issue. Not quite sure, but my mediocre understanding of all this now tells me that it does not happen synchronously so we need more of this GObject.idle_add magic despite it looking wasteful, and it would kind of counteract our attempt to not queue up endless redrawing requests by using atomic(?) operations. If it does not end up in a deadlock or something, that is.

    As I said earlier, there is, apparently, a much simpler way to demonstrate what the issue looks like. One important observation I made: Adding or removing widgets affects the main viewport differently, depending on where the widgets are located. Adding widgets close to the origin (i.e. top-left corner), like menu bar, thumbnailer and toolbar, forces the main viewport to be moved away from the origin, while adding widgets far away from the origin (i.e. lower-right corner), like scroll bars and status bar, appear to be drawn "on top" of the main viewport.

    With this in mind, let's go! Add as many widgets to the main window as possible. Open a single image and make sure that the image is way bigger than the entire window, and that both scroll bars are needed and visible. Scroll to the lower-right corner of the image. Observe that the scroll bars do reflect the current position correctly. Now, press I to hide all widgets. The lower-right corner of the image should still match the lower-right corner of the window. Now, press I again. Observe that the scroll bars indicate that we should still be at the lower-right corner, however, the image in the viewport appears to have moved! Now, try to drag the viewport content around by just a tiny bit. Note how the scroll bars immediately readjust. Redo everything for the next part of the experiment, but instead of dragging the viewport content, now try to drag one of the scroll bars a little bit. Note how now it is the viewport content that jumps around to match the scroll bar. Also, the scroll bar behaves strangely until you drag the viewport content around.

    If you measure by how far everything appears to be off or jumps around, you will probably end up with the same observation as described above.

    By the way, this demonstration works independent of manga mode being enabled or disabled.

    The issue we see in this little experiment seems to have a strong connection with the bug this ticket is about.

    With all that, now, my explanation for what the user can observe looks like this: When switching from one book to another, the GUI is actually redrawn in between when neither the old nor the new book is opened, and since this is just empty space, there is no scroll bars or anything. So, the calculations for the first page of the new book opened are all done with scroll bars disabled. GTK 3 later happily draws the mismatched-looking scroll bars on top of what should be image content, as described above. However, when switching to the next page (using PgDown) which just happens to have similar dimensions like the one we were looking at until a moment ago, the scroll bars are already there. Thus, the calculations will end up with the conclusion that scroll bars are needed (of course, because the image is of the same size as the one earlier), and since they are already there, at least as far as GTK 3 is concerned, they do not need to be redrawn "on top" but can instead be made to reflect the actual value. Also, our layout calculations happen to see the "correct" viewport dimensions on the first iteration. "Correct" as in "actually final, but we could not actually conclude on our own that they will be the correct ones". This might explain why we get to see the values we see in the debug output (see the patches attached in an earlier post).

     
  • Ark

    Ark - 2023-05-07

    Sorry, I hope my previous post is not too convoluted. Now that I wrote about it, I can succinctly express what I think is the underlying issue here:

    Whenever we come to the conclusion that the current layout might be incorrect, we need to redo layout calculations, with GTK 3 immediately telling us the things we need and adjusting scrollbars as we need, and all of this needs to happen atomically.

    However, since it seems to not yet be the case, we run into this kind of bug.

     
  • Ark

    Ark - 2024-02-04

    Now that I think about it, maybe we can work around this issue by adding a new feature. In this case, it would be a new preference so the user can set the width of the viewport scrollbars. With that in place, we do not need to wait for the toolkit to lay out the widgets so we can re-read the viewport's dimensions, but instead, we can calculate everything at once.

    … Which might not actually solve the main issue here. I am not sure. GTK 3 is confusing to me. Help?

     
  • Ark

    Ark - 2024-02-07

    I just glued some snippets together to reduce the code needed to observe the issue. See attached file. The working directory needs to be the directory where the script is located.

    @ferd617 Please have a look at the attached example. It would be great if you (or anyone else) could fix that bug. I already tried some things, as noted in the code, but to no avail.

     
  • Ark

    Ark - 2024-02-07

    Good news! By (almost) pure chance, I stumbled across https://docs.gtk.org/glib/const.PRIORITY_HIGH_IDLE.html where the description just happens to hint at the solution: By changing line 87 to use priority=GLib.PRIORITY_LOW, some revalidation seems to take place before the actual drawing function is being called again. It still needs some fine-tuning, though, since the content is visibly being moved into place now, but at least it looks like I can finally solve this issue!

     

Log in to post a comment.

MongoDB Logo MongoDB