"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