Spirik / GEM

Good Enough Menu for Arduino
GNU Lesser General Public License v3.0
239 stars 36 forks source link

Replace/override sprites for menu decorations? #101

Closed davexre closed 1 month ago

davexre commented 1 month ago

Just making sure I'm not missing something - the sprites for things like leftArrow, rightArrow, and selectArrows are fixed at 6x8 pixels?

My C++ is a little rusty - but I'm thinking that since the sprites are defined as constants, outside of the GEM class (I think they have to be that way, because they have to be PROGMEM?), the only way I can replace them is by editing GEM.cpp (and then not upgrading GEM - or reapplying the changes when I upgrade)? The PROGMEM requirement means they can't be replaced on the fly, either.

I'm using a font that's 14x24 pixels (Adafruit GFX FreeMono12pt7b) - the existing sprites are really small on the screen, so I'd like to replace them with a bigger version of the same sprite.

davexre commented 1 month ago

Edit to note - it's really GEM_adafruit_gfx.cpp that needs to change, in my case... And to note that sprite width is fixed at 8 pixels, looks like - at least, when we draw the Sprite for an item line, it starts drawing at _agfx.width() - 8, rather than something like _agfx.width - arrow.Right[1].width. This also means that scaled sprites always display at 8px wide, here, too (even though the scaled versions are 16px wide).

(my workaround here will just be to make my substitute sprite 8 pixels wide... simple enough!)

Spirik commented 1 month ago

In Adafruit GFX version of GEM there is a method to scale text and icons by scale factor menu.setTextSize(2);. Icons can be scaled up to 2x times only though. What scale factor of 2 does is essentially treats each group of 2x2 pixels as one virtual pixel for text, while graphic bitmaps are displayed properly with 1 to 1 pixel ratio. There are enlarged 12x16 version of icons present in GEM_adafruit_gfx.cpp.

It mostly designed to work with default font, but it appearance may seem blocky (due to 2x pixel ratio). But consider trying this method, may be it will be sufficient for your needs?

If not, there are indeed no method to change icons without editing source code. However, since scaled bitmaps are already present in code (see link above), it should is possible to reuse them with relatively minor adjustments. Try changing this line from byte variant = _textSize > 1 ? 1 : 0; to byte variant = 1; . That way enlarged versions of icons will be drawn regardless of scale factor set via menu.setTextSize() method (and it will be possible to use custom font for menu items w/o scaling this font itself).

davexre commented 1 month ago

I'll give it a go, setting variant manually. I did try setting textSize higher - but it does indeed get super blocky once it's big enough for the display. If I have to swap some sprites, it's not the worst thing in the world - I just have to be careful later, if I update the library (I'll make a patch file and just re-apply it later, if that's the case).

Spirik commented 1 month ago

Sometimes have to remind it to myself, that now there is a way to modify (or extend) GEM functionality without the need to edit source code files! For that you will have to activate Advanced Mode, as described here and here. Then it will be possible to override inner methods of GEM.

In case of setting variant variable value manually it will look something like this:

class GEMProxy : public GEM_adafruit_gfx {
  public:
    using GEM_adafruit_gfx::GEM_adafruit_gfx;

    void drawSprite(int16_t x, int16_t y, const Splash sprite[], uint16_t color) override {
      byte variant = 1;
      _agfx.drawBitmap(x, y, sprite[variant].image, sprite[variant].width, sprite[variant].height, color);
    }
};

GEMProxy menu(tft, GEM_POINTER_ROW, GEM_ITEMS_COUNT_AUTO);

(Haven't tried this code myself - let me know if it will work for you).

davexre commented 1 month ago

A minor change makes it functional - drawSprite should return void.

That does draw the sprites at a much better size, yeah. It still has the same problem I mentioned before, where the sprites on the right side are not drawn wide enough, because they're drawn too far to the right. For example, when we draw the Right Arrow for GEM_ITEM_LINK, the sprint starts 8 pixels from the right edge of the screenm regardless of sprite width (https://github.com/Spirik/GEM/blob/8beda181e0d70099a7f5438579b15961287012a7/src/GEM_adafruit_gfx.cpp#L511).

I can't just override _textSize to multiple that fixed 8 by 2, because that messes w/ the font and various other things.

I looked at overriding printMenuItem, which is marked as GEM_VIRTUAL, but looks like it accesses some protected fields in GEMItem, and would require modification of GEMItem.cpp to add my override object as a friend class there, too. It's missing a few things (the sprites) defined in GEM_adafruit_gfx.cpp, but those could be copied over pretty easily.

The last approach I can try there is to modify the drawSprite override to look at the sprite that's being drawn, and try to guess where it's suppose to start on the X axis, and modify appropriately? ie, if it's rightArrow, make sure it starts 16 pixels inside the edge of the screen, instead of 8?

Really, I think I'm back at modifying GEM_adafruit_gfx.cpp, at least for now. If I were to make a feature suggestion out of this, it would maybe be to make it easier to force sprite sizes to match font size, when using a larger font without changing setTextSize()?

Spirik commented 1 month ago

I think I got it. I've implemented property _spriteSize in addition to _textSize with corresponding method ::setSpriteSize(). That allows to set sprite magnification level (up to 2x) independently of text.

By default, calling menu.setTextSize(2) will set both _textSize and _spriteSize to 2 (the same result as in current public version of GEM). To set only _spriteSize call menu.setSpriteSize(2).

This version is not yet published, however here is PR #102 (and separate branch with updated code). Please, consider checking it out, if you'll get a chance.

davexre commented 1 month ago

That looks like it'll work, yeah. I'll give that a shot in just a bit!

davexre commented 1 month ago

Seems to work perfectly. Thanks, @Spirik !

Spirik commented 1 month ago

Glad to hear! I will create public release tomorrow.

davexre commented 1 month ago

Switched to 1.5.4 - working perfectly! Thanks again for the help!

Spirik commented 1 month ago

Awesome! Closing issue.