@@ -7,7 +7,6 @@
|
||||
#include "MappedInputManager.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
#include "util/MenuItemHelpers.h"
|
||||
|
||||
EpubReaderMenuActivity::EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||
const std::string& title, const int currentPage, const int totalPages,
|
||||
@@ -30,7 +29,7 @@ std::vector<EpubReaderMenuActivity::MenuItem> EpubReaderMenuActivity::buildMenuI
|
||||
std::vector<MenuItem> items;
|
||||
items.reserve(18);
|
||||
// Navigation
|
||||
items.push_back(makeSeparatorMenuItem<MenuItem>(StrId::STR_READER_NAVIGATION));
|
||||
items.push_back({MenuAction::NONE, StrId::STR_READER_NAVIGATION, true});
|
||||
items.push_back({MenuAction::SELECT_CHAPTER, StrId::STR_SELECT_CHAPTER});
|
||||
items.push_back({MenuAction::GO_TO_PERCENT, StrId::STR_GO_TO_PERCENT});
|
||||
if (hasFootnotes) {
|
||||
@@ -39,7 +38,7 @@ std::vector<EpubReaderMenuActivity::MenuItem> EpubReaderMenuActivity::buildMenuI
|
||||
items.push_back({MenuAction::AUTO_PAGE_TURN, StrId::STR_AUTO_TURN_PAGES_PER_MIN});
|
||||
|
||||
// Appearance
|
||||
items.push_back(makeSeparatorMenuItem<MenuItem>(StrId::STR_READER_APPEARANCE));
|
||||
items.push_back({MenuAction::NONE, StrId::STR_READER_APPEARANCE, true});
|
||||
items.push_back({MenuAction::EMBEDDED_STYLE, StrId::STR_EMBEDDED_STYLE});
|
||||
items.push_back({MenuAction::IMAGE_RENDERING, StrId::STR_IMAGES});
|
||||
items.push_back({MenuAction::TEXT_DARKNESS, StrId::STR_TEXT_DARKNESS});
|
||||
@@ -47,13 +46,13 @@ std::vector<EpubReaderMenuActivity::MenuItem> EpubReaderMenuActivity::buildMenuI
|
||||
|
||||
// Synchronisation (only if credentials are set, to avoid confusion)
|
||||
if (KOREADER_STORE.hasCredentials()) {
|
||||
items.push_back(makeSeparatorMenuItem<MenuItem>(StrId::STR_KOREADER_SYNC));
|
||||
items.push_back({MenuAction::NONE, StrId::STR_KOREADER_SYNC, true});
|
||||
items.push_back({MenuAction::PULL_REMOTE, StrId::STR_PULL_PROGRESS_FROM_OTHER_DEVICES});
|
||||
items.push_back({MenuAction::PUSH_LOCAL, StrId::STR_PUSH_PROGRESS_FROM_THIS_DEVICE});
|
||||
}
|
||||
|
||||
// Tools
|
||||
items.push_back(makeSeparatorMenuItem<MenuItem>(StrId::STR_READER_TOOLS));
|
||||
items.push_back({MenuAction::NONE, StrId::STR_READER_TOOLS, true});
|
||||
items.push_back({MenuAction::SCREENSHOT, StrId::STR_SCREENSHOT_BUTTON});
|
||||
items.push_back({MenuAction::DISPLAY_QR, StrId::STR_DISPLAY_QR});
|
||||
items.push_back({MenuAction::DELETE_CACHE, StrId::STR_DELETE_CACHE});
|
||||
@@ -62,7 +61,9 @@ std::vector<EpubReaderMenuActivity::MenuItem> EpubReaderMenuActivity::buildMenuI
|
||||
}
|
||||
|
||||
std::function<bool(int)> EpubReaderMenuActivity::buildSelectablePredicate() const {
|
||||
return makeSelectablePredicate(menuItems);
|
||||
return [this](int index) {
|
||||
return index >= 0 && index < static_cast<int>(menuItems.size()) && !menuItems[index].isSeparator;
|
||||
};
|
||||
}
|
||||
|
||||
void EpubReaderMenuActivity::onEnter() {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <Epub.h>
|
||||
#include <I18n.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -30,12 +29,6 @@ class EpubReaderMenuActivity final : public Activity {
|
||||
DELETE_CACHE
|
||||
};
|
||||
|
||||
struct MenuItem {
|
||||
MenuAction action;
|
||||
StrId labelId;
|
||||
bool isSeparator = false;
|
||||
};
|
||||
|
||||
explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title,
|
||||
const int currentPage, const int totalPages, const int bookProgressPercent,
|
||||
const uint8_t currentOrientation, const bool hasFootnotes,
|
||||
@@ -47,10 +40,17 @@ class EpubReaderMenuActivity final : public Activity {
|
||||
void loop() override;
|
||||
void render(RenderLock&&) override;
|
||||
|
||||
private:
|
||||
struct MenuItem {
|
||||
MenuAction action;
|
||||
StrId labelId;
|
||||
bool isSeparator = false;
|
||||
};
|
||||
|
||||
static std::vector<MenuItem> buildMenuItems(bool hasFootnotes);
|
||||
|
||||
std::function<bool(int)> buildSelectablePredicate() const;
|
||||
|
||||
private:
|
||||
// Fixed menu layout
|
||||
const std::vector<MenuItem> menuItems;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "SyncTimeActivity.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
#include "util/MenuItemHelpers.h"
|
||||
|
||||
namespace {
|
||||
const StrId timeZoneNames[CrossPointSettings::TIMEZONE_COUNT] = {
|
||||
@@ -24,20 +23,22 @@ std::vector<ClockSettingsActivity::MenuItem> ClockSettingsActivity::buildMenuIte
|
||||
std::vector<MenuItem> items;
|
||||
items.reserve(7);
|
||||
// Settings
|
||||
items.push_back(makeSeparatorMenuItem<MenuItem>(StrId::STR_SETTINGS_TITLE));
|
||||
items.push_back({Action::NONE, StrId::STR_SETTINGS_TITLE, true});
|
||||
items.push_back({Action::USE_CLOCK, StrId::STR_USE_CLOCK});
|
||||
items.push_back({Action::CLOCK_FORMAT, StrId::STR_CLOCK_FORMAT});
|
||||
items.push_back({Action::CLOCK_FORMAT, StrId::STR_CLOCK_FORMAT});
|
||||
items.push_back({Action::TIMEZONE, StrId::STR_TIMEZONE});
|
||||
|
||||
// Tools
|
||||
items.push_back(makeSeparatorMenuItem<MenuItem>(StrId::STR_READER_TOOLS));
|
||||
items.push_back({Action::NONE, StrId::STR_READER_TOOLS, true});
|
||||
items.push_back({Action::DETECT_TIMEZONE, StrId::STR_DETECT_TIMEZONE});
|
||||
items.push_back({Action::SYNC_TIME, StrId::STR_SYNC_TIME});
|
||||
items.push_back({Action::SYNC_TIME, StrId::STR_SYNC_TIME});
|
||||
return items;
|
||||
}
|
||||
|
||||
std::function<bool(int)> ClockSettingsActivity::buildSelectablePredicate() const {
|
||||
return makeSelectablePredicate(menuItems);
|
||||
return [this](int index) {
|
||||
return index >= 0 && index < static_cast<int>(menuItems.size()) && !menuItems[index].isSeparator;
|
||||
};
|
||||
}
|
||||
|
||||
void ClockSettingsActivity::onEnter() {
|
||||
@@ -123,7 +124,8 @@ void ClockSettingsActivity::render(RenderLock&&) {
|
||||
const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing * 2;
|
||||
|
||||
GUI.drawList(
|
||||
renderer, Rect{0, contentTop, pageWidth, contentHeight}, static_cast<int>(menuItems.size()), selectedIndex,
|
||||
renderer, Rect{0, contentTop, pageWidth, contentHeight}, static_cast<int>(menuItems.size()),
|
||||
selectedIndex,
|
||||
[this](int index) {
|
||||
const auto title = I18N.get(menuItems[index].labelId);
|
||||
return menuItems[index].isSeparator ? UITheme::makeSeparatorTitle(title) : title;
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
#pragma once
|
||||
#include <I18n.h>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include <I18n.h>
|
||||
|
||||
#include "activities/Activity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
class ClockSettingsActivity final : public Activity {
|
||||
enum class Action { USE_CLOCK, CLOCK_FORMAT, TIMEZONE, SYNC_TIME, DETECT_TIMEZONE, NONE };
|
||||
enum class Action {
|
||||
USE_CLOCK,
|
||||
CLOCK_FORMAT,
|
||||
TIMEZONE,
|
||||
SYNC_TIME,
|
||||
DETECT_TIMEZONE,
|
||||
NONE
|
||||
};
|
||||
|
||||
struct MenuItem {
|
||||
Action action;
|
||||
|
||||
@@ -22,11 +22,9 @@
|
||||
#include "activities/weather/WeatherSettingsActivity.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
#include "util/MenuItemHelpers.h"
|
||||
|
||||
const StrId SettingsActivity::categoryNames[categoryCount] = {StrId::STR_CAT_DISPLAY, StrId::STR_CAT_READER,
|
||||
StrId::STR_CAT_CONTROLS, StrId::STR_CAT_NETWORK,
|
||||
StrId::STR_CAT_SYSTEM};
|
||||
StrId::STR_CAT_CONTROLS, StrId::STR_CAT_SYSTEM};
|
||||
|
||||
void SettingsActivity::onEnter() {
|
||||
Activity::onEnter();
|
||||
@@ -35,9 +33,7 @@ void SettingsActivity::onEnter() {
|
||||
displaySettings.clear();
|
||||
readerSettings.clear();
|
||||
controlsSettings.clear();
|
||||
networkSettings.clear();
|
||||
systemSettings.clear();
|
||||
systemSettings.push_back(SettingInfo::Separator(StrId::STR_SETTINGS_MENU_SETTINGS));
|
||||
|
||||
for (const auto& setting : getSettingsList()) {
|
||||
if (setting.category == StrId::STR_NONE_OPT) continue;
|
||||
@@ -54,8 +50,6 @@ void SettingsActivity::onEnter() {
|
||||
controlsSettings.push_back(setting);
|
||||
} else if (setting.category == StrId::STR_CAT_SYSTEM) {
|
||||
systemSettings.push_back(setting);
|
||||
} else if (setting.category == StrId::STR_CAT_NETWORK) {
|
||||
networkSettings.push_back(setting);
|
||||
}
|
||||
// Web-only categories (KOReader Sync, OPDS Browser) are skipped for device UI
|
||||
}
|
||||
@@ -63,26 +57,15 @@ void SettingsActivity::onEnter() {
|
||||
// Append device-only ACTION items
|
||||
controlsSettings.insert(controlsSettings.begin(),
|
||||
SettingInfo::Action(StrId::STR_REMAP_FRONT_BUTTONS, SettingAction::RemapFrontButtons));
|
||||
|
||||
networkSettings.push_back(SettingInfo::Separator(StrId::STR_SETTINGS_MENU_NETWORK));
|
||||
networkSettings.push_back(SettingInfo::Action(StrId::STR_WIFI_NETWORKS, SettingAction::Network));
|
||||
networkSettings.push_back(SettingInfo::Separator(StrId::STR_SETTINGS_MENU_SOURCES));
|
||||
networkSettings.push_back(SettingInfo::Action(StrId::STR_OPDS_BROWSER, SettingAction::OPDSBrowser));
|
||||
networkSettings.push_back(SettingInfo::Separator(StrId::STR_SETTINGS_MENU_SYNC));
|
||||
networkSettings.push_back(SettingInfo::Action(StrId::STR_KOREADER_SYNC, SettingAction::KOReaderSync));
|
||||
|
||||
// System settings with actions
|
||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_LANGUAGE, SettingAction::Language));
|
||||
|
||||
systemSettings.push_back(SettingInfo::Separator(StrId::STR_SETTINGS_MENU_TOOLS));
|
||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_CLOCK_SETTINGS, SettingAction::ClockSettings));
|
||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_WEATHER_SETTINGS, SettingAction::Weather));
|
||||
|
||||
systemSettings.push_back(SettingInfo::Separator(StrId::STR_SETTINGS_MENU_SYSTEM));
|
||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_WIFI_NETWORKS, SettingAction::Network));
|
||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_KOREADER_SYNC, SettingAction::KOReaderSync));
|
||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_OPDS_BROWSER, SettingAction::OPDSBrowser));
|
||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_CLEAR_READING_CACHE, SettingAction::ClearCache));
|
||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_CHECK_UPDATES, SettingAction::CheckForUpdates));
|
||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_LANGUAGE, SettingAction::Language));
|
||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_SYSTEM_INFO, SettingAction::SystemInfo));
|
||||
|
||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_WEATHER_SETTINGS, SettingAction::Weather));
|
||||
readerSettings.push_back(SettingInfo::Action(StrId::STR_CUSTOMISE_STATUS_BAR, SettingAction::CustomiseStatusBar));
|
||||
|
||||
// Reset selection to first category
|
||||
@@ -92,16 +75,11 @@ void SettingsActivity::onEnter() {
|
||||
// Initialize with first category (Display)
|
||||
currentSettings = &displaySettings;
|
||||
settingsCount = static_cast<int>(displaySettings.size());
|
||||
buttonNavigator.setSelectablePredicate(makeSelectablePredicate(*currentSettings, 1, true), settingsCount + 1);
|
||||
|
||||
// Trigger first update
|
||||
requestUpdate();
|
||||
}
|
||||
|
||||
std::function<bool(int)> SettingsActivity::buildSelectablePredicate() const {
|
||||
return makeSelectablePredicate(*currentSettings, 1, true);
|
||||
}
|
||||
|
||||
void SettingsActivity::onExit() {
|
||||
Activity::onExit();
|
||||
|
||||
@@ -137,12 +115,12 @@ void SettingsActivity::loop() {
|
||||
|
||||
// Handle navigation
|
||||
buttonNavigator.onNextRelease([this] {
|
||||
selectedSettingIndex = buttonNavigator.nextIndex(selectedSettingIndex);
|
||||
selectedSettingIndex = ButtonNavigator::nextIndex(selectedSettingIndex, settingsCount + 1);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousRelease([this] {
|
||||
selectedSettingIndex = buttonNavigator.previousIndex(selectedSettingIndex);
|
||||
selectedSettingIndex = ButtonNavigator::previousIndex(selectedSettingIndex, settingsCount + 1);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
@@ -171,14 +149,10 @@ void SettingsActivity::loop() {
|
||||
currentSettings = &controlsSettings;
|
||||
break;
|
||||
case 3:
|
||||
currentSettings = &networkSettings;
|
||||
break;
|
||||
case 4:
|
||||
currentSettings = &systemSettings;
|
||||
break;
|
||||
}
|
||||
settingsCount = static_cast<int>(currentSettings->size());
|
||||
buttonNavigator.setSelectablePredicate(makeSelectablePredicate(*currentSettings, 1, true), settingsCount + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,9 +163,6 @@ void SettingsActivity::toggleCurrentSetting() {
|
||||
}
|
||||
|
||||
const auto& setting = (*currentSettings)[selectedSetting];
|
||||
if (setting.isSeparator) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (setting.type == SettingType::TOGGLE && setting.valuePtr != nullptr) {
|
||||
// Toggle the boolean value using the member pointer
|
||||
@@ -288,11 +259,7 @@ void SettingsActivity::render(RenderLock&&) {
|
||||
contentRect.height -
|
||||
(metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing * 2)},
|
||||
settingsCount, selectedSettingIndex - 1,
|
||||
[&settings](int index) {
|
||||
const auto title = I18N.get(settings[index].nameId);
|
||||
return settings[index].isSeparator ? UITheme::makeSeparatorTitle(title) : title;
|
||||
},
|
||||
nullptr, nullptr,
|
||||
[&settings](int index) { return std::string(I18N.get(settings[index].nameId)); }, nullptr, nullptr,
|
||||
[&settings](int i) {
|
||||
const auto& setting = settings[i];
|
||||
std::string valueText = "";
|
||||
|
||||
@@ -30,7 +30,6 @@ enum class SettingAction {
|
||||
|
||||
struct SettingInfo {
|
||||
StrId nameId;
|
||||
bool isSeparator = false;
|
||||
SettingType type;
|
||||
uint8_t CrossPointSettings::* valuePtr = nullptr;
|
||||
std::vector<StrId> enumValues;
|
||||
@@ -85,21 +84,11 @@ struct SettingInfo {
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo Action(StrId nameId, SettingAction action, bool isSeparator = false) {
|
||||
static SettingInfo Action(StrId nameId, SettingAction action) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::ACTION;
|
||||
s.action = action;
|
||||
s.isSeparator = isSeparator;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo Separator(StrId nameId) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::ACTION;
|
||||
s.action = SettingAction::None;
|
||||
s.isSeparator = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -166,16 +155,14 @@ class SettingsActivity final : public Activity {
|
||||
std::vector<SettingInfo> displaySettings;
|
||||
std::vector<SettingInfo> readerSettings;
|
||||
std::vector<SettingInfo> controlsSettings;
|
||||
std::vector<SettingInfo> networkSettings;
|
||||
std::vector<SettingInfo> systemSettings;
|
||||
const std::vector<SettingInfo>* currentSettings = nullptr;
|
||||
|
||||
static constexpr int categoryCount = 5;
|
||||
static constexpr int categoryCount = 4;
|
||||
static const StrId categoryNames[categoryCount];
|
||||
|
||||
void enterCategory(int categoryIndex);
|
||||
void toggleCurrentSetting();
|
||||
std::function<bool(int)> buildSelectablePredicate() const;
|
||||
|
||||
public:
|
||||
explicit SettingsActivity(GfxRenderer& renderer, MappedInputManager& mappedInput)
|
||||
|
||||
@@ -98,11 +98,17 @@ Rect UITheme::getContentRect(const GfxRenderer& renderer, bool hasBottomHints, b
|
||||
return Rect{left, top, w - left - right, h - top - bottom};
|
||||
}
|
||||
|
||||
std::string UITheme::makeSeparatorTitle(const std::string& title) { return std::string("__") + title; }
|
||||
std::string UITheme::makeSeparatorTitle(const std::string& title) {
|
||||
return std::string("__") + title;
|
||||
}
|
||||
|
||||
std::string UITheme::makeSeparatorTitle(StrId labelId) { return std::string("__") + I18N.get(labelId); }
|
||||
std::string UITheme::makeSeparatorTitle(StrId labelId) {
|
||||
return std::string("__") + I18N.get(labelId);
|
||||
}
|
||||
|
||||
bool UITheme::isSeparatorTitle(const std::string& title) { return title.rfind("__", 0) == 0; }
|
||||
bool UITheme::isSeparatorTitle(const std::string& title) {
|
||||
return title.rfind("__", 0) == 0;
|
||||
}
|
||||
|
||||
std::string UITheme::stripSeparatorTitle(const std::string& title) {
|
||||
return isSeparatorTitle(title) ? title.substr(2) : title;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <I18n.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <I18n.h>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
#include "components/themes/BaseTheme.h"
|
||||
|
||||
|
||||
@@ -45,9 +45,9 @@ class ButtonNavigator final {
|
||||
[[nodiscard]] static int nextIndex(int currentIndex, const std::vector<bool>& selectable);
|
||||
[[nodiscard]] static int previousIndex(int currentIndex, const std::vector<bool>& selectable);
|
||||
[[nodiscard]] static int nextIndex(int currentIndex, int totalItems,
|
||||
const std::function<bool(int index)>& isSelectable);
|
||||
const std::function<bool(int index)>& isSelectable);
|
||||
[[nodiscard]] static int previousIndex(int currentIndex, int totalItems,
|
||||
const std::function<bool(int index)>& isSelectable);
|
||||
const std::function<bool(int index)>& isSelectable);
|
||||
|
||||
[[nodiscard]] int nextIndex(int currentIndex) const;
|
||||
[[nodiscard]] int previousIndex(int currentIndex) const;
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <I18n.h>
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
// Generic helper for constructing separator rows in menu item vectors.
|
||||
// The item type must have an `action`, an `isSeparator`, and either a `labelId` or `nameId` member.
|
||||
template <typename ItemType>
|
||||
inline ItemType makeSeparatorMenuItem(StrId labelId) {
|
||||
ItemType item{};
|
||||
if constexpr (std::is_member_object_pointer_v<decltype(&ItemType::action)>) {
|
||||
item.action = static_cast<decltype(item.action)>(0);
|
||||
}
|
||||
if constexpr (std::is_member_object_pointer_v<decltype(&ItemType::labelId)>) {
|
||||
item.labelId = labelId;
|
||||
} else if constexpr (std::is_member_object_pointer_v<decltype(&ItemType::nameId)>) {
|
||||
item.nameId = labelId;
|
||||
} else {
|
||||
static_assert(sizeof(ItemType) == 0,
|
||||
"makeSeparatorMenuItem requires ItemType with labelId or nameId member");
|
||||
}
|
||||
item.isSeparator = true;
|
||||
return item;
|
||||
}
|
||||
|
||||
// Generic helper for creating a selectable predicate for menu lists.
|
||||
// The item type must have an `isSeparator` member.
|
||||
template <typename ItemType>
|
||||
inline std::function<bool(int)> makeSelectablePredicate(const std::vector<ItemType>& items) {
|
||||
return
|
||||
[&items](int index) { return index >= 0 && index < static_cast<int>(items.size()) && !items[index].isSeparator; };
|
||||
}
|
||||
|
||||
template <typename ItemType>
|
||||
inline std::function<bool(int)> makeSelectablePredicate(const std::vector<ItemType>& items, int indexOffset,
|
||||
bool firstIndexSelectable) {
|
||||
return [&items, indexOffset, firstIndexSelectable](int index) {
|
||||
if (firstIndexSelectable && index == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const int itemIndex = index - indexOffset;
|
||||
return itemIndex >= 0 && itemIndex < static_cast<int>(items.size()) && !items[itemIndex].isSeparator;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user