In Linux "/.." exists and loops back onto the root. But for the user
it is confusing to see a ".." entry listed that does nothing when
selected, so hide it.
This can happen on a directory with 0 file matches when directories are
not being shown (otherwise there is either ".." or a subdir).
Since there is nothing to be selected, the Accept button is disabled
and no cursor is shown.
Renamed fontHeight to lineHeight, since it is computed using both the
font's line spacing and the height of the folder icon. For the latter,
use the actual icon height plus two pixels spacing instead of the
hardcoded value of 20 (16 height + 4 pixels spacing).
Fixed recently introduced bug where "top" is added to the folder icon
y-coordinate twice. Also center the icon vertically when a large font
is used.
Don't crash if the folder icon is missing. This could only happen on
broken installations though. Also don't search for the icon twice:
SurfaceCollection::skinRes already calls addSkinRes internally when
there is no cached surface for the key.
Removed unused data member selRow.
Keep reference to LinkApp instead of pointer.
Call LinkApp::getSelectorBrowser once and store result.
Renamed fontheight to fontHeight.
Use iY consistently: store the item's y-coordinate in it.
This removes the need for a separate setAction method.
The default action is the empty action, which does nothing. However,
a touch event on a button with the empty action is no longer considered
handled.
Menu::btnContextMenu was changed from a unique_ptr to a plain data
member.
It was only used to fetch resY, so I replaced that by taking the
y-coordinate as an argument. That is also more consistent with the
x-coordinate which was already an argument.
The x-coordinate was changed to a signed int, since that is the norm
for paint coordinates. It can be useful for drawing widgets that are
partially off screen, for example during a (dis)appear animation.
In InputDialog, the ButtonBox field was changed from a pointer to
a plain data member. There was no need to dynamically allocate it.
Previously, IconButton instances to be added to button boxes were
allocated with new, but never freed with delete.
unique_ptr makes sure the buttons will be freed along with the button
box that owns them, or when code calls ButtonBox::clear.
The destructor of ButtonBox has been made redundant by this change, so
it's gone.
Put the drawButton calls left-to-right.
Avoid duplicate code.
The order was changed: "Select" is now always first, to be consistent
with other dialogs.
Instead of correcting the returned coordinate with "- 10" externally,
omit the white space inside the methods.
Note that Font::getTextWidth, which was used until recently, considers
an empty string to have width 1, so 3 + getTextWidth("") + 6 == 10.
There is a difference in how buttons that have neither a label nor an
icon are positioned in the new code, but that is a situation that
should not occur in practice. Plus I'd argue that the new behavior is
actually better in that case.
This was only called form ButtonBox, so I moved the code there.
I still think a paint method shouldn't be repositioning widgets, but
that's something for a later cleanup.
Use emplace_back when a new string is put into a vector.
Removed unused variables.
When searching section names, compare C++ string to C string, so we
don't need to construct a C++ string for the second string just for
the search.
When comparing full strings, operator== will do nicely.
When comparing the first char, "s[0] == c" is more efficient if we know
the string cannot be empty.
Since this surface is created by initBG instead of loaded from skin
search paths, it didn't really fit in SurfaceCollection. After removing
it, one of SurfaceCollection's methods could be removed as well.
This avoids having to do separate getTextWidth calls in several places.
More getTextWidth calls could be saved by splitting the rendering of
the font to an off-screen buffer from the final composition onto the
destination surface: the routines that draw text inside a box have to
compute the width before they can draw the box and currently the box
has to be drawn before the text.
Instead, make the caller perform the lookup. This simplifies the
interface of loadImage and it removes the dependency from
OffscreenSurface on SurfaceCollection.
In theory the timer could expire between the button press that starts
the application launch and the moment the PowerSaver destructor runs.
And I think this does in fact happen occasionally thanks to the CPU
hogging SD controller driver on the GCW Zero, leading to an application
launching with a blanked screen.
The instance-on-demand didn't really work, since we needed explicit
control over this object's destruction to ensure the timer is stopped
when launching an application. And trying to combine getInstance() with
explicit external delete was just ugly.
Use nullptr instead of NULL, evaluate pointer as bool where possible.
Declare the timer callback as a friend function so the methods it uses
can stay private.
Initialize screenState to false; was uninitialized which means that
an initially blanked screen might not be unblanked.
Force screen enable in constructor instead of when timeout is set.
Add removeScreenTimer method.
Include C++-ified versions of the C headers.
This is a BSD extension also present in glibc and uClibc. Not all libc
implementations and not all file systems support it, so we keep the
stat-based code as a fallback.
In the STL, 'at' will perform range checking while operator[] will not.
Since GMenu2X doesn't use exceptions, range checking is not possible,
so 'at' and operator[] were identical.
Previously, if a user installed a new version of an OPK with the same
name as one that had a link configuration file, GMenu2X could request
a file for an application whose new version required no files to
launch.
In practice, this transition would occur only once per OPK application,
when its developer decided to use a custom browser after having used
GMenu2X's file browser. This bug would then show GMenu2X's file browser
to request a file that would not even be passed to the application.
Thanks to Nebuleon for analyzing the problem and the above description.
Browsing the user's wallpaper directory will simply add no new files
to the list of wallpapers available if the directory doesn't exist.
WallpaperDialog::exec doesn't need to care about that.
Because wallpapers are files, not directories, also don't return
directories in the result. The code that makes the wallpapers list
calls FileLister::getFiles and ignores directories anyway.
Browsing the user's translations directory will simply add no new files
to the list of translations available if the directory doesn't exist.
GMenu2X::showSettings() doesn't need to care about that.
Because translations are files, not directories, also don't return
directories in the result. The code that makes the translations list
calls FileLister::getFiles and ignores directories anyway.
A file existence check is already performed, atomically with respect to
the filesystem, by ifstream's constructor. The result of this check is
available using ifstream::is_open().
0 is the default clock value, which doesn't get written to link files
if it's not changed. The clock was previously uninitialised before
reading link files, so it would get written with a random value if
there was none before.
When scanning multiple directories in succession, it's possible that
in some of them no matches are found. Avoid moving all pre-existing
entries back and forth in that case.
Also use move instead of copy for transferring the pre-existing
entries from the vector to the set.
The special case is not necessary for correctness. It would help
performance in theory, but it seems very unlikely that any code would
request a directory scan when it interested in neither the subdirs nor
the files in that directory.
Instead of splitting the filter for every file in FileLister::browse,
the filter is split immediately in FileLister::setFilter, improving
performance.
getFilter is removed because it was unused. This also removes the need
to update the return type to 'const std::vector&' in the method and
rewrite its callers.
Configuration of the FileLister is now left to the subclasses of
BrowseDialog, as the construction of a FileLister and its configuration
(to show or hide directories and files) have been separated.
This allows deleting the destructors of BrowseDialog's subclasses,
which existed solely to delete a FileLister object constructed by
each subclass.
The allocation of a BrowseDialog now also implies allocating enough
space for its FileLister, so remove the check for fl being nullptr in
BrowseDialog::exec().
The constructor now has zero arguments. showDirectories and showFiles
are now set using setter methods.
FileLister::browse is now the sole way to start a scan for files and
directories, replacing the initial path in the constructor and the
setPath method. It allows merging results from multiple directories
as before.
This is all to make explicit how many times the costly task of browsing
a directory is actually carried out.
Instead of showing 'R:', 'G:', 'B:' and 'A:', which take up a lot of space
and can overlap in standard fonts, the selection rectangle surrounding the
number is red, green, blue or gray, and the text is shorter.
This will allow the labels for the skin colors to be longer for translations
as well, given that their values are shown less wide.
Instead of checking which input configuration file exists among 2
choices, then asking InputManager to load that file, InputManager
itself now performs the resolution based on whether ifstream::is_open
returns true for each choice.
The existence of modifications to the skin configuration in the home
directory is now checked with ifstream::is_open, and the system's skin
configuration is used if that returns false.
A file existence check is already performed, atomically with respect to
the filesystem, by ifstream's constructor. The result of this check is
available using ifstream::is_open().
This operation is not so slow that it really needs caching. Also
reducing the delay before showing a directory will have more impact
on the user experience than a slightly faster paint.
The way the caching was implemented was rather problematic. Also I'm
not convinced caching is useful at all. So I removed it. If at some
point we decide that caching is a good idea, it would be best to
re-implement it from scratch, so nothing of value is lost by removing
the existing caching code.
One problem was that the prepare method would check the existence of
a screenshot file for every file in the directory, which adds to the
noticable delay before showing a directory which holds many files.
Another problem is there was no upper limit to the number of
screenshots that would be cached. On directories with many screenshots
the memory use could rise to several hundred megabytes, which can be
problematic on some of the systems we want to support.
Also there was a rather nasty hack present to ensure screenshots were
loaded without an alpha channel.
My main doubt about the usefulness of screenshot caching is that
caching will only speed up the showing of an image from the second
time onwards. In typical use, the average screenshot is displayed at
most once. If the image load time is long enough to annoy the user,
any real solution would have to work also for the first showing.
For example, background (asynchronous) loading could be implemented.
All calls to this method are in GMenu2X::initMenu() and they will only
pass valid indices, so the range check was redundant. Also the return
value was never used.
Added an assert to spot any invalid indices from future code.
There are a few exclusive operations for each type. Also we no longer
need the freeWhenDone flag since the class now determines whether the
surface should be freed or not.
In theory a font could be so large that no full row would fit on the
allotted space; in that case showing a partial row is better than
nothing and will avoid bugs where -1 wraps around on unsigned exprs.
Nebuleon spotted a bug in TextManualDialog where the unsigned value
'pages[page].text.size() - rowsPerPage' could wrap around at 0 for
short chapters. I decided to change a bit more than just fixing the
bug though.
This is the convention that most classes stick to. The likely reason
why Dialog didn't stick to the convention was to be able to provide
a default value for this argument, but that feature wasn't very useful
since every caller already had access to the default surface.
Applications with this flag will not have their stdout/stderr
redirected to a log file when logging is enabled. That is a useful
feature also on platforms where we don't micromanage the console.
Currently the launched application is exec-ed from deep within the menu
code. This means a lot of destructors are never run and as a result
for example file descriptors from the menu remain open in the launched
process.
This is a first step to move the launch invocation from a deep call
stack to the top level.
If a text file ends in a newline, the previous line splitting code
would append an empty line at the end, which looked odd. So now we
explicitly check that a newline is only inserted if more characters
follow.
Setting values are now displayed 10 pixels to the right of setting names, as
passed to MenuSetting::draw.
This commit also contains the following cleanups:
* The height of a row is passed to MenuSetting's draw and touchscreen methods.
* MenuSettingRGBA's magic constant (36) to separate the text for a color's
four components is now a named constant.
* MenuSettingRGBA's color preview squares are now rowHeight - 2 pixels tall,
and have a white border surrounded by a black border to help view the color
it contains in both light and dark themes.
* The rectangle behind the selected setting's name is now drawn by that
setting's drawSelected method.
Setting descriptions and help prompts now appear fully even if they are longer
than the screen allows. Translations do not need to worry about allowed text
being wider than the screen in some fonts anymore.
Instead of reading the file line by line and then concatenating those
lines, just load the entire thing in one go. And pay more attention to
error conditions.
The constructors of those classes now accept a string to be wrapped, instead
of a vector to be modified with split lines inserted into its middle.
Along with this conversion, manuals for applications stored in OPK packages
are now transferred into a string without garbage at the end.
This implementation is based on the implementation in TextDialog::preProcess,
with one major difference: it works on the entire input string, copies it much
less as part of its function, and tries to quickly establish a small search
space for the length of the beginning split of each line.
With most standard fonts and sizes, this means up to 9 computations of metrics
per output line.
Multi-line message boxes had the incorrect height.
I also took the opportunity to make named constants out of magic numbers
making up the various message box dimensions.
This reverts commit 0908aa7bb7.
It turns out there are multi-line text messages in use: Nebuleon found
one in the confirmation message when deleting a section.