@@ -16,9 +16,14 @@ class OptionItem::Impl final {
1616
1717 CCMenuItemToggler* toggler = nullptr ; // The toggler for the option
1818
19+ CCNode* newContainer = nullptr ;
20+
1921 // Save the current state of the toggler as the option state
2022 void saveTogglerState () {
21- if (toggler) options::set (option.getID (), toggler->isToggled (), options::isPinned (option.getID ()));
23+ auto saved = options::get (option.getID ());
24+ if (toggler) options::set (option.getID (), toggler->isToggled (), saved.pin , saved.viewed );
25+
26+ clearNewLabel ();
2227 };
2328
2429 // Notify the user if this option is not compatible for their current platform
@@ -29,6 +34,10 @@ class OptionItem::Impl final {
2934 };
3035 };
3136
37+ void clearNewLabel () {
38+ if (auto label = WeakRef (newContainer).lock ()) label->setVisible (false );
39+ };
40+
3241 constexpr const char * getTierDescString (SillyTier silly, bool compat) noexcept {
3342 if (!compat) return " <cp>OPTION UNAVAILABLE</c>" ;
3443
@@ -134,22 +143,10 @@ bool OptionItem::init(CCSize const& size, Option option, bool devMode) {
134143 addChild (nameLabel);
135144 addChild (categoryLabel);
136145
137- if (devMode) {
138- auto str = fmt::format (" {} | {} delegate(s)" , m_impl->option .getID (), options::getDelegates (m_impl->option .getID ()));
139-
140- auto idLabel = CCLabelBMFont::create (str.c_str (), " chatFont.fnt" , getScaledContentWidth () - 20 .f , kCCTextAlignmentLeft );
141- idLabel->setID (" id-label" );
142- idLabel->setLineBreakWithoutSpace (true );
143- idLabel->setPosition ({x, yCenter - 10 .f });
144- idLabel->setAnchorPoint ({0 .f , 0 .5f });
145- idLabel->setColor (colors::black);
146- idLabel->setOpacity (125 );
147- idLabel->setScale (0 .5f );
148-
149- addChild (idLabel);
150- };
151-
152- auto menuLayout = RowLayout::create ()->setGap (5 .f )->setAxisReverse (true )->setAutoGrowAxis (0 .f );
146+ auto menuLayout = RowLayout::create ()
147+ ->setGap (5 .f )
148+ ->setAxisReverse (true )
149+ ->setAutoGrowAxis (0 .f );
153150
154151 auto menu = CCMenu::create ();
155152 menu->setID (" menu" );
@@ -161,7 +158,7 @@ bool OptionItem::init(CCSize const& size, Option option, bool devMode) {
161158 addChild (menu);
162159
163160 // info button
164- auto helpBtn = Button::createWithSpriteFrameName (
161+ auto infoBtn = Button::createWithSpriteFrameName (
165162 m_impl->compatible ? " GJ_infoIcon_001.png" : " geode.loader/info-alert.png" ,
166163 [this ](Button*) {
167164 m_impl->notifyIncompat ();
@@ -175,11 +172,17 @@ bool OptionItem::init(CCSize const& size, Option option, bool devMode) {
175172 " OK" ,
176173 nullptr ,
177174 375 .f )) popup->show ();
175+
176+ auto saved = options::get (m_impl->option .getID ());
177+ if (!saved.viewed ) {
178+ options::set (m_impl->option .getID (), saved.enabled , saved.pin , true );
179+ m_impl->clearNewLabel ();
180+ };
178181 });
179- helpBtn ->setID (" help -btn" );
180- helpBtn ->setScale (0 .625f );
182+ infoBtn ->setID (" info -btn" );
183+ infoBtn ->setScale (0 .625f );
181184
182- menu->addChild (helpBtn );
185+ menu->addChild (infoBtn );
183186
184187 // @geode-ignore(unknown-resource)
185188 auto pinOff = CCSprite::createWithSpriteFrameName (" geode.loader/pin.png" );
@@ -202,6 +205,36 @@ bool OptionItem::init(CCSize const& size, Option option, bool devMode) {
202205
203206 menu->updateLayout ();
204207
208+ auto newContainerLayout = RowLayout::create ()
209+ ->setGap (1 .25f )
210+ ->setAutoScale (false )
211+ ->setAutoGrowAxis (0 .f );
212+
213+ m_impl->newContainer = CCNode::create ();
214+ m_impl->newContainer ->setID (" new-option-container" );
215+ m_impl->newContainer ->setAnchorPoint ({0 , 0.5 });
216+ m_impl->newContainer ->setPosition ({x, 5 .25f });
217+ m_impl->newContainer ->setLayout (newContainerLayout);
218+
219+ addChild (m_impl->newContainer , 9 );
220+
221+ auto newIcon = CCSprite::createWithSpriteFrameName (" geode.loader/updates-available.png" );
222+ newIcon->setID (" new-option-icon" );
223+ newIcon->setScale (0 .25f );
224+
225+ auto newLabel = CCLabelBMFont::create (" New!" , " bigFont.fnt" );
226+ newLabel->setID (" new-option-label" );
227+ newLabel->setScale (0 .25f );
228+ newLabel->setColor (colors::cyan);
229+
230+ m_impl->newContainer ->addChild (newIcon);
231+ m_impl->newContainer ->addChild (newLabel);
232+
233+ m_impl->newContainer ->updateLayout ();
234+
235+ m_impl->newContainer ->setVisible (!options::isViewed (m_impl->option .getID ()));
236+ m_impl->newContainer ->setScale (0 .75f );
237+
205238 if (!m_impl->compatible ) {
206239 m_impl->toggler ->toggle (false );
207240
@@ -218,21 +251,39 @@ bool OptionItem::init(CCSize const& size, Option option, bool devMode) {
218251 m_impl->saveTogglerState ();
219252 };
220253
254+ if (devMode) {
255+ auto str = fmt::format (" {} | {} delegate(s)" , m_impl->option .getID (), options::getDelegates (m_impl->option .getID ()));
256+
257+ auto idLabel = CCLabelBMFont::create (str.c_str (), " chatFont.fnt" , getScaledContentWidth () - 20 .f , kCCTextAlignmentLeft );
258+ idLabel->setID (" id-label" );
259+ idLabel->setLineBreakWithoutSpace (true );
260+ idLabel->setPosition ({getScaledContentWidth () - 7 .5f , 5 .25f });
261+ idLabel->setAnchorPoint ({1 , 0.5 });
262+ idLabel->setColor (colors::black);
263+ idLabel->setOpacity (125 );
264+ idLabel->setScale (0 .5f );
265+
266+ addChild (idLabel);
267+ };
268+
221269 return true ;
222270};
223271
224272void OptionItem::onToggle (CCObject*) {
225273 if (m_impl->toggler && m_impl->compatible ) {
226274 auto now = !m_impl->toggler ->isToggled ();
227275
228- options::set (m_impl->option .getID (), now, options::isPinned (m_impl->option .getID ()));
276+ auto saved = options::get (m_impl->option .getID ());
277+ options::set (m_impl->option .getID (), now, saved.pin , true );
229278
230279 if (m_impl->option .isRestartRequired ()) {
231280 Notification::create (" Restart required!" , NotificationIcon::Warning, 2 .5f )->show ();
232281 log::warn (" Restart required to apply option {}" , m_impl->option .getID ());
233282 };
234283
235284 log::info (" Option {} now set to {}" , m_impl->option .getName (), now ? " enabled" : " disabled" );
285+
286+ m_impl->clearNewLabel ();
236287 } else if (m_impl->toggler ) {
237288 m_impl->notifyIncompat ();
238289
@@ -242,14 +293,20 @@ void OptionItem::onToggle(CCObject*) {
242293
243294void OptionItem::onPin (CCObject* sender) {
244295 if (auto pinBtn = typeinfo_cast<CCMenuItemToggler*>(sender)) {
245- options::set (m_impl->option .getID (), options::isEnabled (m_impl->option .getID ()), !pinBtn->isToggled ());
296+ options::set (m_impl->option .getID (), options::isEnabled (m_impl->option .getID ()), !pinBtn->isToggled (), true );
246297 PinEvent ().send ();
298+
299+ m_impl->clearNewLabel ();
247300 };
248301};
249302
250- Option const & OptionItem::getOption () const noexcept { return m_impl->option ; };
303+ Option const & OptionItem::getOption () const noexcept {
304+ return m_impl->option ;
305+ };
251306
252- bool OptionItem::isCompatible () const noexcept { return m_impl->compatible ; };
307+ bool OptionItem::isCompatible () const noexcept {
308+ return m_impl->compatible ;
309+ };
253310
254311OptionItem* OptionItem::create (CCSize const & size, Option option, bool devMode) {
255312 auto ret = new OptionItem ();
0 commit comments