Skip to content

Commit d321a06

Browse files
committed
#236 theme builder and grid layout fixes, example improvements. More logging.
1 parent 31003f7 commit d321a06

12 files changed

+127
-30
lines changed

examples/arduino32/stm32DuinoDemo/RawCustomDrawing.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class MyCustomDrawing : public CustomDrawing {
2020
}
2121

2222
void started(BaseMenuRenderer *currentRenderer) override {
23-
// called once when the take-over display is started before calling renderLoop so you can set things up.
23+
// called once when the take-over display is started before calling renderLoop so you can set things up.
2424
switches.getEncoder()->changePrecision(100, 50);
2525
}
2626

@@ -35,7 +35,7 @@ class MyCustomDrawing : public CustomDrawing {
3535
}
3636
else if(++ticks % 10 == 1) {
3737
// Why write your own code using device drawable? The main reason is, that it works exactly the same over
38-
// adafruit, u8g2 and TFTeSPI with a moderately complete API.
38+
// Adafruit, PicoSDK, FrameBuffer, u8g2 and TFTeSPI with a moderately complete API.
3939
DeviceDrawable *dd = dev.getDeviceDrawable();
4040
dd->startDraw();
4141
const Coord &dims = dd->getDisplayDimensions();

examples/arduino32/stm32DuinoDemo/stm32DuinoDemo.ino

+36-15
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
* are available. By default it is setup for an OLED screen and a rotary encoder, although it could be moved to use
44
* many other different display and input technologies.
55
*
6+
* It demonstrates the use of the card menu layout, where items are shown one at a time as you scroll through them,
7+
* configuring some items to use larger than usual fonts, and other items to render as icons. It also has an ethernet
8+
* network remote included too.
9+
*
610
* Getting started: https://www.thecoderscorner.com/products/arduino-libraries/tc-menu/tcmenu-overview-quick-start/
711
*/
812

@@ -49,12 +53,15 @@ const uint8_t standardNetMask[] = { 255, 255, 255, 0 };
4953
// 2. https://tcmenu.github.io/documentation/arduino-libraries/tc-menu/themes/rendering-with-themes-icons-grids/
5054

5155
// here we provide two title widgets, for ethernet connection, and client connection
52-
TitleWidget widgetConnection(iconsConnection, 2, 16, 12, nullptr);
53-
TitleWidget widgetEthernet(iconsEthernetConnection, 2, 16, 12, &widgetConnection);
56+
TitleWidget widgetConnection(iconsConnection, 2, 16, 12);
57+
TitleWidget widgetEthernet(iconsEthernetConnection, 2, 16, 12);
5458

5559
color_t defaultCardPalette[] = {1, 0, 1, 1};
5660

5761
void overrideDrawingForMainMenu(TcThemeBuilder& themeBuilder) {
62+
// now we make the two settings and status menus use icons instead of regular drawing, and the numbered menu items
63+
// 33,45,78 to use a larger font so it takes the entire screen.
64+
5865
// we're going to use this a few times so declare once
5966
const Coord iconSize(APPICONS_WIDTH, APPICONS_HEIGHT);
6067

@@ -105,32 +112,44 @@ void overrideDrawingForMainMenu(TcThemeBuilder& themeBuilder) {
105112
}
106113

107114
void overrideDrawingForCardMenu(TcThemeBuilder& themeBuilder) {
108-
// override every single item on the card menu to have larger font and different padding/justification
115+
// override back button on the card menu to be the default 32x32 back icon
116+
themeBuilder.menuItemOverride(menuBackStatusCards)
117+
.withJustification(tcgfx::GridPosition::JUSTIFY_CENTER_WITH_VALUE)
118+
.withPadding(MenuPadding(2))
119+
.withPalette(defaultCardPalette)
120+
.withImageXbmp(Coord(32, 32), defaultBackIconBitmap)
121+
.onRow(0) // <== if you prefer that the back item is not first, you can change its order here
122+
.apply();
123+
124+
// override every single item on the card sub menu to have larger font and different padding/justification
109125
themeBuilder.submenuPropertiesActionOverride(menuStatusCards)
110126
.withJustification(tcgfx::GridPosition::JUSTIFY_CENTER_NO_VALUE)
111-
.withNativeFont(u8g2_font_inr33_mn, 1)
127+
.withNativeFont(u8g2_font_inb16_mf, 1)
112128
.withPadding(MenuPadding(2))
113129
.apply();
114130
}
115131

116-
void setupGridLayoutForCardView() {
132+
void setupCardLayoutAndWidgets() {
117133
// create a theme builder to help us configure how to draw.
118134
TcThemeBuilder themeBuilder(renderer);
119135

120136
// enable card layout providing the left and right icons. This enables for root menu
121137
themeBuilder.enableCardLayoutWithXbmImages(Coord(11, 22), ArrowHoriz11x22BitmapLeft, ArrowHoriz11x22BitmapRight, true)
122138
.setMenuAsCard(menuStatusCards, true);
123139

124-
// see the method above where we override drawing for all items that are in card layout.
140+
// these two functions, defined directly above this one configure the icons and special text arrangements for
141+
// the items in the card layouts.
125142
overrideDrawingForMainMenu(themeBuilder);
126143
overrideDrawingForCardMenu(themeBuilder);
127144

128-
// now we make the two settings and status menus use icons instead of regular drawing.
145+
themeBuilder.addingTitleWidget(widgetEthernet)
146+
.addingTitleWidget(widgetConnection);
147+
148+
// You must always call apply after doing anything with theme builder.
129149
themeBuilder.apply();
130150

131151
// now we set the title widgets that appear on the top right, for the link status we check the ethernet library
132152
// status every half second and update the widget to represent that status.
133-
renderer.setFirstWidget(&widgetEthernet);
134153
taskManager.scheduleFixedRate(500, [] {
135154
widgetEthernet.setCurrentState(Ethernet.linkStatus() == LinkON ? 1 : 0);
136155
});
@@ -148,17 +167,18 @@ using namespace tcremote;
148167

149168
void setup() {
150169
// This example logs using IoLogging, see the following guide to enable
151-
// https://www.thecoderscorner.com/products/arduino-libraries/io-abstraction/arduino-logging-with-io-logging/
170+
// https://tcmenu.github.io/documentation/arduino-libraries//io-abstraction/arduino-logging-with-io-logging/
152171
IOLOG_START_SERIAL
153172
serEnableLevel(SER_NETWORK_DEBUG, true);
173+
serEnableLevel(SER_TCMENU_DEBUG, true);
154174

155-
// Start up serial and prepare the correct SPI, your pins may differ
175+
// Start up serial and prepare the correct SPI, your pins or method of doing this may differ
156176
SPI.setMISO(PB4);
157177
SPI.setMOSI(PB5);
158178
SPI.setSCLK(PB3);
159179

160-
//
161-
// Here we start up the Stm32Ethernet library for STM32 boards/chips with built-in ethernet
180+
// Here we start up the Stm32Ethernet library for STM32 boards/chips with built-in ethernet, again you could
181+
// easily change this for your own boards network arrangements
162182
Ethernet.begin();
163183
Serial.print("My IP address is ");
164184
Ethernet.localIP().printTo(Serial);
@@ -167,13 +187,14 @@ void setup() {
167187
// and then run the menu setup
168188
setupMenu();
169189

170-
// now load back values from EEPROM, but only when we can read the confirmatory magic key, see EEPROM loading in the docs
190+
// now load back values from EEPROM, but only when we can read the confirmatory magic key, see EEPROM docs:
191+
// https://tcmenu.github.io/documentation/arduino-libraries/tc-menu/menu-eeprom-integrations/
171192
menuMgr.load(0xd00d, [] {
172193
// this gets called when the menu hasn't been saved before, to initialise the first time.
173194
menuDecimal.setCurrentValue(4);
174195
});
175196

176-
// here we register the custom drawing we created earlier with the renderer
197+
// here we register the custom drawing created in `RawCustomDrawing.h`
177198
myCustomDrawing.registerWithRenderer();
178199

179200
// We can set a callback for when the title item is pressed on the main menu, here we just take over the display
@@ -185,7 +206,7 @@ void setup() {
185206
menuRuntimesCustomList.setNumberOfRows(10);
186207

187208
// now we set up the layouts to make the card view look right.
188-
setupGridLayoutForCardView();
209+
setupCardLayoutAndWidgets();
189210
}
190211

191212
void loop() {

src/BaseRenderers.h

+1
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ class BaseMenuRenderer : public MenuRenderer, Executable {
450450
* @param the first widget in a chain of widgets linked by next pointer.
451451
*/
452452
void setFirstWidget(TitleWidget* widget);
453+
TitleWidget* getFirstWidget() { return this->firstWidget; }
453454

454455
/**
455456
* Called when the menu has been altered, to reset the countdown to

src/MenuItems.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ uint8_t MenuItem::copyNameToBuffer(char* buf, int offset, int size) const {
8080

8181
uint16_t MenuItem::getId() const
8282
{
83-
if (info == nullptr) {
83+
if (info == nullptr || menuType == MENUTYPE_BACK_VALUE) {
8484
return asRuntimeItem(this)->getRuntimeId();
8585
}
8686

src/RuntimeMenuItem.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
#include "tcUtil.h"
1616

1717
/** For items that dont need to have the same id each time (such as back menu items), we just randomly give them an ID */
18-
#define RANDOM_ID_START 50000
18+
#ifndef RANDOM_ID_START
19+
#define RANDOM_ID_START 30000
20+
#endif
1921

2022
/** For items that dont need to have the same id each time (such as back menu items), we just randomly give them an ID */
2123
menuid_t nextRandomId();

src/graphics/BaseGraphicalRenderer.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,9 @@ void BaseGraphicalRenderer::recalculateDisplayOrder(MenuItem *root, bool safeMod
323323
if(root->getMenuType() == MENUTYPE_BACK_VALUE) {
324324
serlogF2(SER_TCMENU_DEBUG, "Handling back item", root->getId());
325325
auto* myProps = getDisplayPropertiesFactory().configFor(root, ItemDisplayProperties::COMPTYPE_TITLE);
326-
itemOrderByRow.add(GridPositionRowCacheEntry(root, GridPosition(GridPosition::DRAW_TITLE_ITEM, myProps->getDefaultJustification(), 0), myProps));
326+
auto *confGrid = getDisplayPropertiesFactory().gridPositionForItem(item);
327+
auto pos = (confGrid) ? confGrid->getPosition() : GridPosition(GridPosition::DRAW_TITLE_ITEM, myProps->getDefaultJustification(), 0);
328+
itemOrderByRow.add(GridPositionRowCacheEntry(root, pos, myProps));
327329
item = root->getNext();
328330
}
329331

src/graphics/GfxMenuConfig.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,17 @@ void prepareDefaultGfxConfig(ColorGfxMenuConfig<void*>* config) {
6666
ItemDisplayProperties *ConfigurableItemDisplayPropertiesFactory::configFor(MenuItem *pItem, ItemDisplayProperties::ComponentType compType) {
6767
// make sure that we never return null, in the worst case, provide a default row for this.
6868
if(displayProperties.count()==0) {
69+
serlogF(SER_WARNING, "No theme set");
6970
color_t defaultColors[] = { RGB(255,255,255), RGB(0,0,0), RGB(192, 192, 192), RGB(255, 255, 255)};
7071
setDrawingPropertiesDefault(ItemDisplayProperties::COMPTYPE_ITEM, defaultColors, MenuPadding(2), nullptr,
7172
1, 2, 12, GridPosition::JUSTIFY_TITLE_LEFT_VALUE_RIGHT, MenuBorder());
7273
}
7374

7475
ItemDisplayProperties *pConf = nullptr;
7576
if(pItem != nullptr) {
76-
pConf = displayProperties.getByKey(MakePropsKey(pItem->getId(), false, compType));
77+
auto key = MakePropsKey(pItem->getId(), false, compType);
78+
pConf = displayProperties.getByKey(key);
79+
serlogF3(SER_TCMENU_DEBUG, "Find by key: ", key, pConf != nullptr);
7780
if(pConf) return pConf;
7881
}
7982
return configForCurrentSub(compType);
@@ -90,9 +93,13 @@ ItemDisplayProperties *ConfigurableItemDisplayPropertiesFactory::configForCurren
9093
ItemDisplayProperties *pConf = nullptr;
9194
auto sub = menuMgr.getCurrentSubMenu();
9295
uint16_t subId = sub ? sub->getId() : 0;
93-
pConf = displayProperties.getByKey(MakePropsKey(subId, true, compType));
96+
uint32_t key = MakePropsKey(subId, true, compType);
97+
98+
pConf = displayProperties.getByKey(key);
99+
serlogF3(SER_TCMENU_DEBUG, "Find key sub: ", key, pConf != nullptr);
94100
if(pConf) return pConf;
95101

102+
serlogF(SER_TCMENU_DEBUG, "Find use def");
96103
pConf = displayProperties.getByKey(MakePropsKey(MENUID_NOTSET, false, compType));
97104
if(pConf) return pConf;
98105

src/graphics/GfxMenuConfig.h

+11-4
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ namespace tcgfx {
455455
#define MENUID_NOTSET 0xffff
456456

457457
inline uint32_t MakePropsKey(uint16_t menuId, bool parentKey, ItemDisplayProperties::ComponentType ty) {
458-
return (uint32_t)menuId | (parentKey ? 0x10000UL : 0UL) | ((uint32_t)ty << 18UL);
458+
return ((uint32_t)menuId) | (parentKey ? 0x10000UL : 0UL) | ((uint32_t)ty << 18UL);
459459
}
460460

461461
/**
@@ -587,7 +587,10 @@ namespace tcgfx {
587587
*/
588588
void setDrawingPropertiesDefault(ItemDisplayProperties::ComponentType drawing, const color_t* palette, MenuPadding pad, const void *font, uint8_t mag,
589589
uint8_t spacing, uint8_t requiredHeight, GridPosition::GridJustification defaultJustification, MenuBorder border) {
590-
setDrawingProperties(MakePropsKey(MENUID_NOTSET, false, drawing), palette, pad, font, mag, spacing, requiredHeight, defaultJustification, border);
590+
591+
auto key = MakePropsKey(MENUID_NOTSET, false, drawing);
592+
serlogF2(SER_TCMENU_DEBUG, "Add def prop: ", key);
593+
setDrawingProperties(key, palette, pad, font, mag, spacing, requiredHeight, defaultJustification, border);
591594
}
592595

593596
/**
@@ -605,7 +608,9 @@ namespace tcgfx {
605608
*/
606609
void setDrawingPropertiesForItem(ItemDisplayProperties::ComponentType drawing, uint16_t id, const color_t* palette, MenuPadding pad, const void *font, uint8_t mag,
607610
uint8_t spacing, uint8_t requiredHeight, GridPosition::GridJustification defaultJustification, MenuBorder border) {
608-
setDrawingProperties(MakePropsKey(id, false, drawing), palette, pad, font, mag, spacing, requiredHeight, defaultJustification, border);
611+
auto key = MakePropsKey(id, false, drawing);
612+
serlogF3(SER_TCMENU_DEBUG, "Add item prop: ", key, id);
613+
setDrawingProperties(key, palette, pad, font, mag, spacing, requiredHeight, defaultJustification, border);
609614
}
610615

611616
/**
@@ -623,7 +628,9 @@ namespace tcgfx {
623628
*/
624629
void setDrawingPropertiesAllInSub(ItemDisplayProperties::ComponentType drawing, uint16_t id, const color_t* palette, MenuPadding pad, const void *font, uint8_t mag,
625630
uint8_t spacing, uint8_t requiredHeight, GridPosition::GridJustification defaultJustification, MenuBorder border) {
626-
setDrawingProperties(MakePropsKey(id, true, drawing), palette, pad, font, mag, spacing, requiredHeight, defaultJustification, border);
631+
auto key = MakePropsKey(id, true, drawing);
632+
serlogF3(SER_TCMENU_DEBUG, "Add sub prop: ", key, id);
633+
setDrawingProperties(key, palette, pad, font, mag, spacing, requiredHeight, defaultJustification, border);
627634
}
628635

629636
/**

src/graphics/GraphicsDeviceRenderer.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ namespace tcgfx {
466466
forceDrawWidgets = true;
467467
}
468468
if (titleNeeded && (locRedrawMode == MENUDRAW_COMPLETE_REDRAW || titleEntry->getMenuItem()->isChanged(displayNumber))) {
469-
bool active = titleNeeded && activeItem == titleEntry->getMenuItem();
469+
bool active = activeItem == titleEntry->getMenuItem();
470470
drawMenuItem(titleEntry, Coord(0, 0), cardLayoutPane->getTitleSize(), DrawingFlags(true, active, menuMgr.getCurrentEditor() == titleEntry->getMenuItem()));
471471
forceDrawWidgets = true;
472472
} else {
@@ -478,7 +478,7 @@ namespace tcgfx {
478478
int offsetY = (cardLayoutPane->getMenuSize().y - int(entry->getHeight())) / 2;
479479
Coord menuStart(cardLayoutPane->getMenuLocation().x, cardLayoutPane->getMenuLocation().y + offsetY);
480480
Coord menuSize(cardLayoutPane->getMenuSize().x, int(entry->getHeight()));
481-
bool active = titleNeeded && activeItem == entry->getMenuItem();
481+
bool active = activeItem == entry->getMenuItem();
482482
drawMenuItem(entry, menuStart, menuSize, DrawingFlags(false, active, menuMgr.getCurrentEditor() == entry->getMenuItem()));
483483
}
484484
cardLayoutPane->prepareAndPaintButtons(this, activeIndex, itemOrderByRow.count(), titleMode != NO_TITLE);

src/graphics/TcThemeBuilder.cpp

+27-1
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,22 @@ TcThemeBuilder& TcThemeBuilder::withPalette(const color_t *cols) {
100100
}
101101

102102
ThemePropertiesBuilder &TcThemeBuilder::menuItemOverride(MenuItem &item) {
103-
ItemDisplayProperties::ComponentType ty = (isItemActionable(&item)) ? ItemDisplayProperties::COMPTYPE_ACTION : ItemDisplayProperties::COMPTYPE_ITEM;
103+
ItemDisplayProperties::ComponentType ty = ItemDisplayProperties::COMPTYPE_ITEM;
104+
if(isItemActionable(&item)) {
105+
ty = ItemDisplayProperties::COMPTYPE_ACTION;
106+
} else if(item.getMenuType() == MENUTYPE_TITLE_ITEM || item.getMenuType() == MENUTYPE_BACK_VALUE) {
107+
ty = ItemDisplayProperties::COMPTYPE_TITLE;
108+
}
104109
propertiesBuilder.initForLevel(this, ty, ThemePropertiesBuilder::THEME_ITEM, &item);
105110
return propertiesBuilder;
106111
}
107112

113+
ThemePropertiesBuilder &TcThemeBuilder::menuItemOverride(MenuItem &item, ItemDisplayProperties::ComponentType componentType) {
114+
propertiesBuilder.initForLevel(this, componentType, ThemePropertiesBuilder::THEME_ITEM, &item);
115+
return propertiesBuilder;
116+
}
117+
118+
108119
TcThemeBuilder& TcThemeBuilder::enableCardLayoutWithXbmImages(Coord iconSize, const uint8_t *leftIcon, const uint8_t *rightIcon, bool isMono) {
109120
auto left = new DrawableIcon(-1, iconSize, tcgfx::DrawableIcon::ICON_XBITMAP, leftIcon, nullptr);
110121
auto right = new DrawableIcon(-1, iconSize, tcgfx::DrawableIcon::ICON_XBITMAP, rightIcon, nullptr);
@@ -141,4 +152,19 @@ TcThemeBuilder& TcThemeBuilder::manualDimensions(int x, int y) {
141152
return *this;
142153
}
143154

155+
TcThemeBuilder &TcThemeBuilder::addingTitleWidget(TitleWidget &theWidget) {
156+
TitleWidget *pWidget = renderer.getFirstWidget();
157+
if(pWidget == nullptr) {
158+
renderer.setFirstWidget(&theWidget);
159+
} else {
160+
int loopCount = 0;
161+
while(pWidget->getNext() != nullptr && ++loopCount < 10) {
162+
pWidget = pWidget->getNext();
163+
}
164+
pWidget->setNext(&theWidget);
165+
renderer.redrawRequirement(MENUDRAW_COMPLETE_REDRAW);
166+
}
167+
return *this;
168+
}
169+
144170

src/graphics/TcThemeBuilder.h

+15-1
Original file line numberDiff line numberDiff line change
@@ -461,11 +461,23 @@ namespace tcgfx {
461461

462462
/**
463463
* Get access to theme properties for a specific menu item, with this you can change how an item renders, and
464-
* even apply different grid settings.
464+
* even apply different grid settings. This guesses the component type in most cases but if you want to force a
465+
* particular component type see the override method with the second parameter.
466+
* @param item the menu item to override drawing
465467
* @return a theme properties builder
466468
*/
467469
ThemePropertiesBuilder &menuItemOverride(MenuItem &item);
468470

471+
/**
472+
* Get access to theme properties for a specific menu item, with this you can change how an item renders, and
473+
* even apply different grid settings. This specifically sets the component type for situations where it cannot
474+
* be easily guessed.
475+
* @param item the menu item to override drawing.
476+
* @param componentType one of `COMPTYPE_TITLE`, `COMPTYPE_ITEM`, `COMPTYPE_ACTION`
477+
* @return a theme properties builder
478+
*/
479+
ThemePropertiesBuilder &menuItemOverride(MenuItem &item, ItemDisplayProperties::ComponentType componentType);
480+
469481
/**
470482
* Get access to theme properties for a sub menu, with this you can change how all items in that submenu render
471483
* This applies to regular items
@@ -551,6 +563,8 @@ namespace tcgfx {
551563
* @return reference to itself for chaining
552564
*/
553565
TcThemeBuilder& setMenuAsCard(SubMenuItem& item, bool on);
566+
567+
TcThemeBuilder& addingTitleWidget(TitleWidget& theWidget);
554568

555569
/**
556570
* Call after you've finished configuring your theme, this forces a refresh and ensures it presents properly.

src/stockIcons/directionalIcons.h

+17
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,21 @@ const uint8_t ArrowHoriz11x22BitmapRight[] PROGMEM = {
3434
0x03,0x00,0x01,0x00
3535
};
3636

37+
/**
38+
* defaultBackIconBitmap - the default back icon that can be used with card layout. It is 32x32 in size.
39+
*/
40+
// XBM_LSB_FIRST width=32, height=32, size=128
41+
// auto size = Coord(32, 32);
42+
const uint8_t defaultBackIconBitmap[] PROGMEM = {
43+
0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xf0,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0xfc,0x00,0x00,
44+
0x00,0xff,0x00,0x00,0x80,0xff,0x00,0x00,0xe0,0xff,0x03,0x00,0xf0,0xff,0x3f,0x00,0xfc,0xff,0xff,0x00,
45+
0xfe,0xff,0xff,0x03,0xff,0xff,0xff,0x0f,0xff,0xff,0xff,0x1f,0xfe,0xff,0xff,0x3f,0xfc,0xff,0xff,0x3f,
46+
0xf0,0xff,0xff,0x7f,0xe0,0xff,0xff,0x7f,0x80,0xff,0x00,0x7f,0x00,0xff,0x00,0x7c,0x00,0xfc,0x00,0x70,
47+
0x00,0xf8,0x00,0x60,0x00,0xf0,0x00,0x40,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x78,
48+
0x00,0x00,0x01,0x40,0x00,0x00,0x01,0x40,0x00,0x00,0x01,0x40,0x00,0x00,0x61,0x43,0x00,0x00,0x61,0x43,
49+
0x00,0x00,0x0f,0x78,0x00,0x00,0x00,0x00
50+
};
51+
52+
53+
3754
#endif //TCMENU_DIRECTIONAL_ICONS_H

0 commit comments

Comments
 (0)