"Fossies" - the Fresh Open Source Software Archive 
Member "tdesktop-2.6.0/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_controls.mm" (23 Feb 2021, 6971 Bytes) of package /linux/misc/tdesktop-2.6.0.tar.gz:
As a special service "Fossies" has tried to format the requested text file into HTML format (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "mac_touchbar_controls.mm" see the
Fossies "Dox" file reference documentation.
1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "platform/mac/touchbar/mac_touchbar_controls.h"
9
10 #ifndef OS_OSX
11
12 #include "base/platform/mac/base_utilities_mac.h" // Q2NSString()
13 #include "core/sandbox.h" // Sandbox::customEnterFromEventLoop()
14 #include "ui/text/format_values.h" // Ui::FormatDurationText()
15 #include "media/audio/media_audio.h"
16 #include "platform/mac/touchbar/mac_touchbar_common.h"
17
18 #import <AppKit/NSButton.h>
19 #import <AppKit/NSCustomTouchBarItem.h>
20 #import <AppKit/NSImage.h>
21 #import <AppKit/NSImageView.h>
22 #import <AppKit/NSSlider.h>
23 #import <AppKit/NSSliderTouchBarItem.h>
24
25 using namespace TouchBar;
26
27 namespace {
28
29 constexpr auto kPadding = 7;
30
31 inline NSImage *Icon(const style::icon &icon) {
32 return CreateNSImageFromStyleIcon(icon, kCircleDiameter / 2);
33 }
34
35 inline NSDictionary *Attributes() {
36 return @{
37 NSFontAttributeName: [NSFont systemFontOfSize:14],
38 NSParagraphStyleAttributeName:
39 [NSMutableParagraphStyle defaultParagraphStyle],
40 NSForegroundColorAttributeName: [NSColor whiteColor]
41 };
42 }
43
44 inline NSString *FormatTime(TimeId time) {
45 return Platform::Q2NSString(Ui::FormatDurationText(time));
46 }
47
48 } // namespace
49
50 #pragma mark - TrackPosition
51
52 @interface TrackPosition : NSImageView
53 @end // @interface TrackPosition
54
55 @implementation TrackPosition {
56 NSMutableString *_text;
57
58 double _width;
59 double _height;
60
61 rpl::lifetime _lifetime;
62 }
63
64 - (id)init:(rpl::producer< Media::Player::TrackState>)trackState {
65 self = [super init];
66 const auto textLength = _lifetime.make_state<rpl::variable<int>>(0);
67 _width = _height = 0;
68 _text = [[NSMutableString alloc] initWithCapacity:13];
69
70 rpl::combine(
71 rpl::duplicate(
72 trackState
73 ) | rpl::map([](const auto &state) {
74 return state.position / 1000;
75 }) | rpl::distinct_until_changed(),
76 std::move(
77 trackState
78 ) | rpl::map([](const auto &state) {
79 return state.length / 1000;
80 }) | rpl::distinct_until_changed()
81 ) | rpl::start_with_next([=](int position, int length) {
82 [_text setString:[NSString stringWithFormat:@"%@ / %@",
83 FormatTime(position),
84 FormatTime(length)]];
85 *textLength = _text.length;
86
87 [self display];
88 }, _lifetime);
89
90 textLength->changes(
91 ) | rpl::start_with_next([=] {
92 const auto size = [_text sizeWithAttributes:Attributes()];
93 _width = size.width + kPadding * 2;
94 _height = size.height;
95
96 if (self.image) {
97 [self.image release];
98 }
99 self.image = [[NSImage alloc] initWithSize:NSMakeSize(
100 _width,
101 kCircleDiameter)];
102 }, _lifetime);
103
104 return self;
105 }
106
107 - (void)drawRect:(NSRect)dirtyRect {
108 if (!(_text && _text.length && _width && _height)) {
109 return;
110 }
111 const auto size = [_text sizeWithAttributes:Attributes()];
112 const auto rect = CGRectMake(
113 (_width - size.width) / 2,
114 -(kCircleDiameter - _height) / 2,
115 _width,
116 kCircleDiameter);
117 [_text drawInRect:rect withAttributes:Attributes()];
118 }
119
120 - (void)dealloc {
121 if (self.image) {
122 [self.image release];
123 }
124 if (_text) {
125 [_text release];
126 }
127 [super dealloc];
128 }
129
130 @end // @implementation TrackPosition
131
132 namespace TouchBar {
133
134 NSButton *CreateTouchBarButton(
135 // const style::icon &icon,
136 NSImage *image,
137 rpl::lifetime &lifetime,
138 Fn<void()> callback) {
139 id block = [^{
140 Core::Sandbox::Instance().customEnterFromEventLoop(callback);
141 } copy];
142
143 NSButton* button = [NSButton
144 buttonWithImage:image
145 target:block
146 action:@selector(invoke)];
147 lifetime.add([=] {
148 [block release];
149 });
150 return button;
151 }
152
153 NSButton *CreateTouchBarButton(
154 const style::icon &icon,
155 rpl::lifetime &lifetime,
156 Fn<void()> callback) {
157 return CreateTouchBarButton(Icon(icon), lifetime, std::move(callback));
158 }
159
160 NSButton *CreateTouchBarButtonWithTwoStates(
161 NSImage *icon1,
162 NSImage *icon2,
163 rpl::lifetime &lifetime,
164 Fn<void(bool)> callback,
165 bool firstState,
166 rpl::producer<bool> stateChanged) {
167 NSButton* button = [NSButton
168 buttonWithImage:(firstState ? icon2 : icon1)
169 target:nil
170 action:nil];
171
172 const auto isFirstState = lifetime.make_state<bool>(firstState);
173 id block = [^{
174 const auto state = *isFirstState;
175 button.image = state ? icon1 : icon2;
176 *isFirstState = !state;
177 Core::Sandbox::Instance().customEnterFromEventLoop([=] {
178 callback(state);
179 });
180 } copy];
181
182 button.target = block;
183 button.action = @selector(invoke);
184
185 std::move(
186 stateChanged
187 ) | rpl::start_with_next([=](bool isChangedToFirstState) {
188 button.image = isChangedToFirstState ? icon1 : icon2;
189 }, lifetime);
190
191 lifetime.add([=] {
192 [block release];
193 });
194 return button;
195 }
196
197 NSButton *CreateTouchBarButtonWithTwoStates(
198 const style::icon &icon1,
199 const style::icon &icon2,
200 rpl::lifetime &lifetime,
201 Fn<void(bool)> callback,
202 bool firstState,
203 rpl::producer<bool> stateChanged) {
204 return CreateTouchBarButtonWithTwoStates(
205 Icon(icon1),
206 Icon(icon2),
207 lifetime,
208 std::move(callback),
209 firstState,
210 std::move(stateChanged));
211 }
212
213 NSSliderTouchBarItem *CreateTouchBarSlider(
214 NSString *itemId,
215 rpl::lifetime &lifetime,
216 Fn<void(bool, double, double)> callback,
217 rpl::producer<Media::Player::TrackState> stateChanged) {
218 const auto lastDurationMs = lifetime.make_state<crl::time>(0);
219
220 auto *seekBar = [[NSSliderTouchBarItem alloc] initWithIdentifier:itemId];
221 seekBar.slider.minValue = 0.0f;
222 seekBar.slider.maxValue = 1.0f;
223 seekBar.customizationLabel = @"Seek Bar";
224
225 id block = [^{
226 // https://stackoverflow.com/a/45891017
227 auto *event = [[NSApplication sharedApplication] currentEvent];
228 const auto touchUp = [event
229 touchesMatchingPhase:NSTouchPhaseEnded
230 inView:nil].count > 0;
231 Core::Sandbox::Instance().customEnterFromEventLoop([=] {
232 callback(touchUp, seekBar.slider.doubleValue, *lastDurationMs);
233 });
234 } copy];
235
236 std::move(
237 stateChanged
238 ) | rpl::start_with_next([=](const Media::Player::TrackState &state) {
239 const auto stop = Media::Player::IsStoppedOrStopping(state.state);
240 const auto duration = double(stop ? 0 : state.length);
241 auto slider = seekBar.slider;
242 if (duration <= 0) {
243 slider.enabled = false;
244 slider.doubleValue = 0;
245 } else {
246 slider.enabled = true;
247 if (!slider.highlighted) {
248 const auto pos = stop
249 ? 0
250 : std::max(state.position, int64(0));
251 slider.doubleValue = (pos / duration) * slider.maxValue;
252 *lastDurationMs = duration;
253 }
254 }
255 }, lifetime);
256
257 seekBar.target = block;
258 seekBar.action = @selector(invoke);
259 lifetime.add([=] {
260 [block release];
261 });
262
263 return seekBar;
264 }
265
266 NSCustomTouchBarItem *CreateTouchBarTrackPosition(
267 NSString *itemId,
268 rpl::producer<Media::Player::TrackState> stateChanged) {
269 auto *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:itemId];
270 auto *trackPosition = [[[TrackPosition alloc]
271 init:std::move(stateChanged)] autorelease];
272
273 item.view = trackPosition;
274 item.customizationLabel = @"Track Position";
275 return item;
276 }
277
278 } // namespace TouchBar
279
280 #endif // OS_OSX