6
6
#include < GLFW/glfw3.h>
7
7
8
8
#include < cage-core/hashString.h>
9
+ #include < cage-core/ini.h>
9
10
#include < cage-core/string.h>
10
11
#include < cage-core/texts.h>
11
12
#include < cage-engine/guiBuilder.h>
@@ -135,6 +136,26 @@ namespace cage
135
136
136
137
using Matcher = std::variant<std::monostate, KeyboardMatcher, ModifiersMatcher, MouseMatcher, WheelMatcher>;
137
138
139
+ CAGE_FORCE_INLINE bool operator ==(const Matcher &a, const Matcher &b)
140
+ {
141
+ if (a.index () != b.index ())
142
+ return false ;
143
+ return std::visit (
144
+ [](const auto &a, const auto &b) -> bool
145
+ {
146
+ using T = std::decay_t <decltype (a)>;
147
+ if constexpr (std::is_same_v<T, std::monostate>)
148
+ return true ;
149
+ else
150
+ {
151
+ // using operator == leads to stack overflow
152
+ static_assert (std::is_trivially_copyable_v<T>);
153
+ return detail::memcmp (&a, &b, sizeof (T)) == 0 ;
154
+ }
155
+ },
156
+ a, b);
157
+ }
158
+
138
159
CAGE_FORCE_INLINE KeybindModesFlags matches (const GenericInput &input, const Matcher &matcher)
139
160
{
140
161
return std::visit (
@@ -238,24 +259,42 @@ namespace cage
238
259
return maker.result ;
239
260
}
240
261
262
+ const KeybindCreateConfig &validateConfig (const KeybindCreateConfig &config)
263
+ {
264
+ CAGE_ASSERT (!config.id .empty ());
265
+ CAGE_ASSERT (!findKeybind (config.id )); // must be unique
266
+ CAGE_ASSERT (none (config.requiredFlags & config.forbiddenFlags ));
267
+ CAGE_ASSERT (any (config.devices ));
268
+ CAGE_ASSERT (none (config.devices & KeybindDevicesFlags::WheelRoll) || none (config.devices & KeybindDevicesFlags::WheelScroll)); // these two flags are mutually exclusive
269
+ CAGE_ASSERT (none (config.devices & KeybindDevicesFlags::Modifiers) || config.devices == KeybindDevicesFlags::Modifiers); // modifiers is exclusive with all other flags
270
+ CAGE_ASSERT (any (config.modes ));
271
+ return config;
272
+ }
273
+
274
+ std::vector<Matcher> makeDefaults (const KeybindCreateConfig &config, PointerRange<const GenericInput> defaults)
275
+ {
276
+ std::vector<Matcher> res;
277
+ for (const auto &it : defaults)
278
+ {
279
+ const auto mt = makeMatcher (config, it);
280
+ if (!std::holds_alternative<std::monostate>(mt))
281
+ res.push_back (mt);
282
+ }
283
+ CAGE_ASSERT (res.size () == defaults.size ());
284
+ return res;
285
+ }
286
+
241
287
bool guiUpdateGlobal (uintPtr ptr, const GenericInput &);
242
288
243
289
EventListener<bool (const GenericInput &)> assignmentListener;
244
290
245
291
class KeybindImpl : public Keybind
246
292
{
247
293
public:
248
- KeybindImpl (const KeybindCreateConfig &config, PointerRange<const GenericInput> defaults, Delegate<bool (const GenericInput &)> event) : config(config), defaults(defaults.begin(), defaults.end()), textId(HashString(config.id))
249
- {
250
- CAGE_ASSERT (!config.id .empty ());
251
- CAGE_ASSERT (!findKeybind (config.id )); // must be unique
252
- CAGE_ASSERT (none (config.requiredFlags & config.forbiddenFlags ));
253
- CAGE_ASSERT (any (config.devices ));
254
- CAGE_ASSERT (none (config.devices & KeybindDevicesFlags::WheelRoll) || none (config.devices & KeybindDevicesFlags::WheelScroll)); // these two flags are mutually exclusive
255
- CAGE_ASSERT (none (config.devices & KeybindDevicesFlags::Modifiers) || config.devices == KeybindDevicesFlags::Modifiers); // modifiers is exclusive with all other flags
256
- CAGE_ASSERT (any (config.modes ));
294
+ KeybindImpl (const KeybindCreateConfig &config_, PointerRange<const GenericInput> defaults_, Delegate<bool (const GenericInput &)> event_) : config(validateConfig(config_)), defaults(makeDefaults(config_, defaults_)), textId(HashString(config.id))
295
+ {
257
296
reset (); // make matchers from the defaults
258
- this ->event = event ;
297
+ this ->event = event_ ;
259
298
global ().push_back (this );
260
299
}
261
300
@@ -318,12 +357,11 @@ namespace cage
318
357
319
358
const KeybindCreateConfig config;
320
359
EventListener<bool (const GenericInput &)> listener;
321
- const std::vector<GenericInput > defaults;
360
+ const std::vector<Matcher > defaults;
322
361
std::vector<Matcher> matchers;
323
362
const uint32 textId = 0 ;
324
363
mutable bool active = false ; // allows tick events
325
364
mutable bool autoDeactivate = false ; // automatically deactivates after first engine tick
326
-
327
365
Entity *guiEnt = nullptr ;
328
366
uint32 assigningIndex = m;
329
367
@@ -347,7 +385,7 @@ namespace cage
347
385
{
348
386
auto _ = g->rightRow (0.5 );
349
387
g->button ().disabled (count () == 0 ).event (Delegate<bool (const GenericInput &)>().bind <KeybindImpl, &KeybindImpl::guiClear>(this )).image (HashString (" cage/texture/keybindClear.png" )).tooltip <HashString (" cage/keybinds/clear" ), " Clear" >().size (Vec2 (28 ));
350
- g->button ().event (Delegate<bool (const GenericInput &)>().bind <KeybindImpl, &KeybindImpl::guiReset>(this )).image (HashString (" cage/texture/keybindReset.png" )).tooltip <HashString (" cage/keybinds/reset" ), " Reset" >().size (Vec2 (28 ));
388
+ g->button ().disabled (defaults == matchers). event (Delegate<bool (const GenericInput &)>().bind <KeybindImpl, &KeybindImpl::guiReset>(this )).image (HashString (" cage/texture/keybindReset.png" )).tooltip <HashString (" cage/keybinds/reset" ), " Reset" >().size (Vec2 (28 ));
351
389
}
352
390
}
353
391
@@ -537,10 +575,7 @@ namespace cage
537
575
void Keybind::reset ()
538
576
{
539
577
KeybindImpl *impl = (KeybindImpl *)this ;
540
- clear ();
541
- for (const auto &it : impl->defaults )
542
- add (it);
543
- CAGE_ASSERT (impl->matchers .size () == impl->defaults .size ());
578
+ impl->matchers = impl->defaults ;
544
579
}
545
580
546
581
Holder<Keybind> newKeybind (const KeybindCreateConfig &config, const GenericInput &defaults, Delegate<bool (const GenericInput &)> event)
@@ -601,15 +636,117 @@ namespace cage
601
636
}
602
637
}
603
638
639
+ namespace
640
+ {
641
+ String toString (const Matcher &mt)
642
+ {
643
+ return std::visit (
644
+ [](const auto &a) -> String
645
+ {
646
+ using T = std::decay_t <decltype (a)>;
647
+ // if constexpr (std::is_same_v<T, std::monostate>)
648
+ // return "";
649
+ if constexpr (std::is_same_v<T, KeyboardMatcher>)
650
+ return Stringizer () + " key " + a.key + " " + (uint32)a.requiredFlags + " " + (uint32)~a.forbiddenFlags ;
651
+ if constexpr (std::is_same_v<T, ModifiersMatcher>)
652
+ return Stringizer () + " mods " + (uint32)a.requiredFlags + " " + (uint32)~a.forbiddenFlags ;
653
+ if constexpr (std::is_same_v<T, MouseMatcher>)
654
+ return Stringizer () + " mouse " + (uint32)a.button + " " + (uint32)a.requiredFlags + " " + (uint32)~a.forbiddenFlags ;
655
+ if constexpr (std::is_same_v<T, WheelMatcher>)
656
+ return Stringizer () + " wheel " + a.direction + " " + (uint32)a.requiredFlags + " " + (uint32)~a.forbiddenFlags ;
657
+ },
658
+ mt);
659
+ }
660
+
661
+ MatcherBase baseFromString (String &s)
662
+ {
663
+ const uint32 r = toUint32 (split (s));
664
+ const uint32 f = toUint32 (split (s));
665
+ return MatcherBase{ (ModifiersFlags)r, ~(ModifiersFlags)f };
666
+ }
667
+
668
+ Matcher fromString (const String &str)
669
+ {
670
+ if (str.empty ())
671
+ return {};
672
+ String s = str;
673
+ const String type = split (s);
674
+ if (type == " key" )
675
+ {
676
+ const uint32 k = toUint32 (split (s));
677
+ return KeyboardMatcher{ baseFromString (s), k };
678
+ }
679
+ else if (type == " mods" )
680
+ {
681
+ return ModifiersMatcher{ baseFromString (s) };
682
+ }
683
+ else if (type == " mouse" )
684
+ {
685
+ const uint32 b = toUint32 (split (s));
686
+ return MouseMatcher{ baseFromString (s), (MouseButtonsFlags)b };
687
+ }
688
+ else if (type == " wheel" )
689
+ {
690
+ const sint32 d = toUint32 (split (s));
691
+ return WheelMatcher{ baseFromString (s), d };
692
+ }
693
+ else
694
+ return {};
695
+ }
696
+ }
697
+
604
698
Holder<Ini> keybindsExport ()
605
699
{
606
- // todo
607
- return {};
700
+ Holder<Ini> ini = newIni ();
701
+ for (KeybindImpl *k : global ())
702
+ {
703
+ ini->set (k->config .id , " count" , Stringizer () + k->matchers .size ());
704
+ uint32 i = 0 ;
705
+ for (const Matcher &mt : k->matchers )
706
+ {
707
+ ini->set (k->config .id , Stringizer () + i, toString (mt));
708
+ i++;
709
+ }
710
+ }
711
+ return ini;
608
712
}
609
713
610
- void keybindsImport (const Ini *ini)
714
+ void keybindsImport (const Ini *ini, bool updateGui )
611
715
{
612
- // todo
716
+ for (KeybindImpl *k : global ())
717
+ {
718
+ try
719
+ {
720
+ if (ini->sectionExists (k->config .id ))
721
+ {
722
+ k->clear ();
723
+ for (const String &it : ini->items (k->config .id ))
724
+ {
725
+ if (it != " count" )
726
+ k->matchers .push_back (fromString (ini->get (k->config .id , it)));
727
+ }
728
+ }
729
+ else
730
+ k->reset ();
731
+ if (updateGui)
732
+ k->makeGui ();
733
+ }
734
+ catch (...)
735
+ {
736
+ CAGE_LOG (SeverityEnum::Warning, " keybinds" , Stringizer () + " error importing keybinds for: " + k->config .id );
737
+ k->reset ();
738
+ }
739
+ }
740
+ }
741
+
742
+ void keybindsResetAll (bool updateGui)
743
+ {
744
+ for (KeybindImpl *k : global ())
745
+ {
746
+ k->reset ();
747
+ if (updateGui)
748
+ k->makeGui ();
749
+ }
613
750
}
614
751
615
752
KeybindModesFlags inputKeybindMode (const GenericInput &in)
0 commit comments