HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
language.hpp
1// Copyright Take Vos 2020.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "../cast.hpp"
8#include "../utils.hpp"
9#include "../log.hpp"
10#include "../subsystem.hpp"
11#include "../notifier.hpp"
12#include "../timer.hpp"
13#include "language_tag.hpp"
14#include <string>
15#include <vector>
16#include <functional>
17#include <mutex>
18
19namespace tt {
20
21
22class language {
23public:
24 using callback_ptr_type = typename notifier<void()>::callback_ptr_type;
25
26 language_tag tag;
27 std::function<int(int)> plurality_func;
28
29 language(language_tag tag) noexcept;
30
31 language(language const &) = delete;
32 language(language &&) = delete;
33 language &operator=(language const &) = delete;
34 language &operator=(language &&) = delete;
35
36 [[nodiscard]] ssize_t plurality(long long n, ssize_t max) const noexcept {
37 int r;
38 if (plurality_func) {
39 r = plurality_func(narrow_cast<int>(n % 1'000'000));
40 } else {
41 // Use English as fallback.
42 r = static_cast<int>(n == 1);
43 }
44 return std::clamp(narrow_cast<ssize_t>(r), ssize_t{0}, max - 1);
45 }
46
47 [[nodiscard]] static language *find(language_tag const &tag) noexcept {
48 ttlet lock = std::scoped_lock(_mutex);
49
50 ttlet i = _languages.find(tag);
51 if (i != _languages.end()) {
52 return i->second.get();
53 } else {
54 return nullptr;
55 }
56 }
57
58 [[nodiscard]] static std::vector<language *> preferred_languages()
59 {
60 if (start_subsystem(_is_running, false, subsystem_init, subsystem_deinit)) {
61 ttlet lock = std::scoped_lock(_mutex);
62 return _preferred_languages;
63 } else {
64 return {};
65 }
66 }
67
68 [[nodiscard]] static language &find_or_create(language_tag const &tag) noexcept
69 {
70 ttlet lock = std::scoped_lock(_mutex);
71
72 auto *r = find(tag);
73 if (!r) {
74 auto tmp = std::make_unique<language>(tag);
75 r = tmp.get();
76 _languages[tag] = std::move(tmp);
77 }
78 return *r;
79 }
80
85 {
87
88 auto prev_short_tag = language_tag{};
89 for (ttlet &tag: tags) {
90 ttlet short_tag = tag.short_tag();
91
92 if (prev_short_tag && short_tag != prev_short_tag) {
93 if (std::find(r.cbegin(), r.cend(), prev_short_tag) == r.cend()) {
94 r.push_back(prev_short_tag);
95 }
96 }
97
98 if (std::find(r.cbegin(), r.cend(), tag) == r.cend()) {
99 r.push_back(tag);
100 }
101
102 prev_short_tag = short_tag;
103 }
104
105 if (prev_short_tag) {
106 if (std::find(r.cbegin(), r.cend(), prev_short_tag) == r.cend()) {
107 r.push_back(prev_short_tag);
108 }
109 }
110 return r;
111 }
112
113 static void set_preferred_languages(std::vector<language_tag> tags) noexcept
114 {
115 ttlet lock = std::scoped_lock(_mutex);
116
117 auto tmp = std::vector<language*>{};
118 for (ttlet &tag: add_short_names(tags)) {
119 tmp.push_back(&find_or_create(tag));
120 }
121
122 if (compare_then_assign(_preferred_languages, tmp)) {
123 auto language_order_string = std::string{};
124 for (ttlet &language : tmp) {
125 if (language_order_string.size() != 0) {
126 language_order_string += ", ";
127 }
128 language_order_string += to_string(language->tag);
129 }
130 tt_log_info("Setting preferred language in order: ", language_order_string);
131 }
132 }
133
138
139 [[nodiscard]] static callback_ptr_type subscribe(callback_ptr_type const &callback) noexcept
140 {
141 return _notifier.subscribe(callback);
142 }
143
144 template<typename Callback> requires(std::is_invocable_v<Callback>)
145 [[nodiscard]] static callback_ptr_type subscribe(Callback &&callback) noexcept
146 {
147 return _notifier.subscribe(std::forward<Callback>(callback));
148 }
149
150 static void unsubscribe(callback_ptr_type const &callback) noexcept
151 {
152 _notifier.unsubscribe(callback);
153 }
154
155private:
156 inline static std::atomic<bool> _is_running;
158 inline static std::vector<language_tag> _preferred_language_tags;
159 inline static std::vector<language *> _preferred_languages;
160 inline static std::recursive_mutex _mutex;
161 inline static notifier<void()> _notifier;
162 inline static typename timer::callback_ptr_type _languages_maintenance_callback;
163
164 [[nodiscard]] static bool subsystem_init() noexcept
165 {
166 using namespace std::literals::chrono_literals;
167
168 _languages_maintenance_callback = timer::global().add_callback(5s, [](auto...) {
169 ttlet new_preferred_language_tags = language::read_os_preferred_languages();
170
171 if (language::_preferred_language_tags != new_preferred_language_tags) {
172 language::_preferred_language_tags = new_preferred_language_tags;
173 set_preferred_languages(language::_preferred_language_tags);
174 language::_notifier();
175 }
176 }, true);
177
178 return true;
179 }
180
181 static void subsystem_deinit() noexcept
182 {
183 if (_is_running.exchange(false)) {
184
185 }
186 }
187};
188
189}
Definition notifier.hpp:18
Definition language.hpp:22
static std::vector< language_tag > add_short_names(std::vector< language_tag > tags) noexcept
Add short language names to the list of names.
Definition language.hpp:84
static std::vector< language_tag > read_os_preferred_languages() noexcept
Get the preferred language tags from the operating system.
An IETF BCP 47 Language tag.
Definition language_tag.hpp:15
T cbegin(T... args)
T cend(T... args)
T exchange(T... args)
T find(T... args)
T move(T... args)
T push_back(T... args)
T to_string(T... args)