diff --git a/Easydict/objc/ViewController/Window/WindowManager/EZWindowManager.m b/Easydict/objc/ViewController/Window/WindowManager/EZWindowManager.m index 273ab8de8..b89302dd4 100644 --- a/Easydict/objc/ViewController/Window/WindowManager/EZWindowManager.m +++ b/Easydict/objc/ViewController/Window/WindowManager/EZWindowManager.m @@ -67,57 +67,60 @@ - (void)setup { self.screen = NSScreen.mainScreen; self.floatingWindowTypeArray = [NSMutableArray arrayWithArray:@[ @(EZWindowTypeNone) ]]; self.actionType = EZActionTypeInvokeQuery; - + self.eventMonitor = [EZEventMonitor shared]; [self setupEventMonitor]; } - (void)setupEventMonitor { [self.eventMonitor startMonitor]; - + mm_weakify(self); [self.eventMonitor setSelectedTextBlock:^(NSString *_Nonnull selectedText) { mm_strongify(self); - -// MMLogInfo(@"auto get selected text successfully: %@", selectedText.truncated); - + + // MMLogInfo(@"auto get selected text successfully: %@", selectedText.truncated); + self.selectedText = selectedText ?: @""; self.actionType = self.eventMonitor.actionType; - + // !!!: Record current selected start and end point, eventMonitor's startPoint will change every valid event. self.startPoint = self.eventMonitor.startPoint; self.endPoint = self.eventMonitor.endPoint; - + CGPoint point = [self getPopButtonWindowLocation]; // This is top-left point CGPoint bottomLeftPoint = CGPointMake(point.x, point.y - self.popButtonWindow.height); CGPoint safePoint = [EZCoordinateUtils getFrameSafePoint:self.popButtonWindow.frame moveToPoint:bottomLeftPoint inScreen:self.screen]; + + safePoint = [self getSafePointForPopButtonWindow:safePoint]; + [self.popButtonWindow setFrameOrigin:safePoint]; - + [self.popButtonWindow orderFrontRegardless]; // Set a high level to make sure it's always on top of other windows, such as PopClip. self.popButtonWindow.level = kCGScreenSaverWindowLevel; }]; - + [self updatePopButtonQueryAction]; - + [self.eventMonitor setLeftMouseDownBlock:^(CGPoint clickPoint) { mm_strongify(self); self.startPoint = clickPoint; self.screen = [EZCoordinateUtils screenForPoint:clickPoint]; EZLayoutManager.shared.screen = self.screen; }]; - + [self.eventMonitor setRightMouseDownBlock:^(CGPoint clickPoint) { mm_strongify(self); self.screen = [EZCoordinateUtils screenForPoint:clickPoint]; EZLayoutManager.shared.screen = self.screen; }]; - + [self.eventMonitor setDismissPopButtonBlock:^{ mm_strongify(self); [self.popButtonWindow close]; }]; - + [self.eventMonitor setDismissAllNotPinndFloatingWindowBlock:^{ mm_strongify(self); if (self->_miniWindow) { @@ -127,7 +130,7 @@ - (void)setupEventMonitor { [self closeFloatingWindowIfNotPinnedOrMain:EZWindowTypeFixed]; } }]; - + [self.eventMonitor setDoubleCommandBlock:^{ NSLog(@"double command block"); }]; @@ -137,17 +140,17 @@ - (void)setupEventMonitor { /// Update pop button query action. - (void)updatePopButtonQueryAction { mm_weakify(self); - + EZButton *popButton = self.popButtonWindow.popButton; Configuration *config = [Configuration shared]; - + if (config.hideMainWindow) { // FIXME: Click pop button will also show preferences window. [popButton setClickBlock:^(EZButton *button) { mm_strongify(self); [self popButtonWindowClicked]; }]; - + if (config.clickQuery) { popButton.mouseEnterBlock = nil; } else { @@ -158,7 +161,7 @@ - (void)updatePopButtonQueryAction { } } else { popButton.clickBlock = nil; - + [popButton setMouseEnterBlock:^(EZButton *button) { mm_strongify(self); [self popButtonWindowClicked]; @@ -306,14 +309,14 @@ - (void)showFloatingWindowType:(EZWindowType)windowType completionHandler:(nullable void (^)(void))completionHandler { self.selectedText = queryText; self.actionType = actionType; - + MMLogInfo(@"show floating windowType: %ld, queryText: %@, autoQuery: %d, actionType: %@, atPoint: %@", windowType, queryText, autoQuery, actionType, @(point)); - + // Update isTextEditable value when using invoke query, such as open URL Scheme by PopClip. if (actionType == EZActionTypeInvokeQuery) { [self.eventMonitor updateSelectedTextEditableState]; } - + EZBaseQueryWindow *window = [self windowWithType:windowType]; // If window is pinned now, we don't need to change it @@ -327,54 +330,52 @@ - (void)showFloatingWindowType:(EZWindowType)windowType if (!queryText) { /** In some applications, in extreme cases, using shortcut to get the text fails causing text is nil, in which case we display a tips view. - + https://github.com/tisfeng/Easydict/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98#%E4%B8%BA%E4%BB%80%E4%B9%88%E5%9C%A8%E6%9F%90%E4%BA%9B%E5%BA%94%E7%94%A8%E4%B8%AD%E5%8F%96%E8%AF%8D%E6%96%87%E6%9C%AC%E4%B8%BA%E7%A9%BA */ - if (!Configuration.shared.disableTipsView - && !Configuration.shared.keepPrevResultWhenEmpty - && actionType == EZActionTypeShortcutQuery) { + if (!Configuration.shared.disableTipsView && !Configuration.shared.keepPrevResultWhenEmpty && actionType == EZActionTypeShortcutQuery) { [queryViewController showTipsView:YES]; } - + // !!!: location is top-left point, so we need to change it to bottom-left point. CGPoint newPoint = CGPointMake(point.x, point.y - window.height); [queryViewController updateActionType:self.actionType]; [self showFloatingWindow:window atPoint:newPoint]; - + if (completionHandler) { completionHandler(); } - + return; } - + // Log selected text when querying. [self logSelectedTextEvent]; - + void (^updateQueryTextAndStartQueryBlock)(BOOL) = ^(BOOL needFocus) { // Update input text and detect. [queryViewController updateQueryTextAndParagraphStyle:queryText actionType:self.actionType]; [queryViewController detectQueryText:nil]; - + if (needFocus) { // Order front and focus floating window. [self orderFrontWindowAndFocusInputTextView:window]; } - + if (autoQuery) { [queryViewController startQueryText:queryText actionType:self.actionType]; } - + // TODO: Maybe we should remove this option, it seems useless. if ([Configuration.shared autoCopySelectedText]) { [queryText copyToPasteboard]; } - + if (completionHandler) { completionHandler(); } }; - + if (!window.isPin) { // Reset tableView and window height first, avoid being affected by previous window height. [queryViewController resetTableView:^{ @@ -392,7 +393,7 @@ - (void)showFloatingWindowType:(EZWindowType)windowType - (void)orderFrontWindowAndFocusInputTextView:(EZBaseQueryWindow *)window { [self saveFrontmostApplication]; - + // Focus floating window. [window makeKeyAndOrderFront:nil]; [window.queryViewController focusInputTextView]; @@ -406,38 +407,38 @@ - (void)detectQueryText:(NSString *)text completion:(nullable void (^)(NSString - (void)showFloatingWindow:(EZBaseQueryWindow *)window atPoint:(CGPoint)point { // MMLogInfo(@"show floating window: %@, %@", window, @(point)); - + [self saveFrontmostApplication]; - + if (Snip.shared.isSnapshotting) { return; } - + [[self currentShowingSettingsWindow] close]; - + // get safe window position CGPoint safeLocation = [EZCoordinateUtils getFrameSafePoint:window.frame moveToPoint:point inScreen:self.screen]; [window setFrameOrigin:safeLocation]; window.level = EZFloatingWindowLevel; - + // FIXME: need to optimize. We have to remove main window temporarily, and `orderBack:` when closed floating window. // But `orderBack:` will cause the query window to fail to display in stage manager mode (#385) if ([EZMainQueryWindow isAlive]) { [_mainWindow orderOut:nil]; } - + // MMLogInfo(@"window frame: %@", @(window.frame)); - + // ???: This code will cause warning: [Window] Warning: Window EZFixedQueryWindow 0x107f04db0 ordered front from a non-active application and may order beneath the active application's windows. [window makeKeyAndOrderFront:nil]; - + /// ???: orderFrontRegardless will cause OCR show blank window when window has shown. // [window orderFrontRegardless]; - + // !!!: Focus input textView should behind makeKeyAndOrderFront:, otherwise it will not work in the first time. [window.queryViewController focusInputTextView]; - + [self updateFloatingWindowType:window.windowType isShowing:YES]; } @@ -448,19 +449,19 @@ - (nullable NSWindow *)currentShowingSettingsWindow { return window; } } - + return nil; } - (void)updateFloatingWindowType:(EZWindowType)floatingWindowType isShowing:(BOOL)isShowing { NSNumber *windowType = @(floatingWindowType); -// MMLogInfo(@"update windowType: %@, isShowing: %d", windowType, isShowing); -// MMLogInfo(@"before floatingWindowTypeArray: %@", self.floatingWindowTypeArray); + // MMLogInfo(@"update windowType: %@, isShowing: %d", windowType, isShowing); + // MMLogInfo(@"before floatingWindowTypeArray: %@", self.floatingWindowTypeArray); [self.floatingWindowTypeArray removeObject:windowType]; [self.floatingWindowTypeArray insertObject:windowType atIndex:isShowing ? 0 : 1]; - -// MMLogInfo(@"after floatingWindowTypeArray: %@", self.floatingWindowTypeArray); + + // MMLogInfo(@"after floatingWindowTypeArray: %@", self.floatingWindowTypeArray); } - (void)updateWindowsTitlebarButtonsToolTip { @@ -471,7 +472,7 @@ - (void)updateWindowsTitlebarButtonsToolTip { - (NSScreen *)getMouseLocatedScreen { NSPoint mouseLocation = [NSEvent mouseLocation]; // ???: self.endPoint - + // 找到鼠标所在屏幕 NSScreen *screen = [NSScreen.screens mm_find:^id(NSScreen *_Nonnull obj, NSUInteger idx) { return NSPointInRect(mouseLocation, obj.frame) ? obj : nil; @@ -482,7 +483,7 @@ - (NSScreen *)getMouseLocatedScreen { return MMPointInRect(mouseLocation, obj.frame) ? obj : nil; }]; } - + return screen; } @@ -490,41 +491,41 @@ - (NSScreen *)getMouseLocatedScreen { /// TODO: need to optimize. - (CGPoint)getPopButtonWindowLocation { NSPoint location = [NSEvent mouseLocation]; -// MMLogInfo(@"mouseLocation: (%.1f, %.1f)", location.x, location.y); - + // MMLogInfo(@"mouseLocation: (%.1f, %.1f)", location.x, location.y); + if (CGPointEqualToPoint(location, CGPointZero)) { return CGPointZero; } - + NSPoint startLocation = self.startPoint; NSPoint endLocation = self.endPoint; - + // Direction from left to right. BOOL isDirectionRight = endLocation.x >= startLocation.x; // Direction from top to bottom. BOOL isDirectionDown = YES; - + CGFloat minLineHeight = 20; - + CGFloat deltaY = endLocation.y - startLocation.y; // Direction up. if (deltaY > minLineHeight / 2) { isDirectionDown = NO; isDirectionRight = NO; } - + CGFloat x = location.x; CGFloat y = location.y; - + // self.offsetPoint is (15, -15) - + x += self.offsetPoint.x; y += self.offsetPoint.y; - + // FIXME: If adjust y when Direction is Up, it will cause some UI bugs 😢 // TODO: This codo is too ugly, need to optimize. - - + + // if (isDirectionDown) { // x += self.offsetPoint.x; // y += self.offsetPoint.y; @@ -533,29 +534,29 @@ - (CGPoint)getPopButtonWindowLocation { // // Direction up, show pop button window above the selected text. // y = location.y - self.offsetPoint.y + self.popButtonWindow.height + 5; // } - + // CGRect selectedTextFrame = self.eventMonitor.selectedTextFrame; // MMLogInfo(@"selected text frame: %@", NSStringFromRect(selectedTextFrame)); // MMLogInfo(@"start point: %@", NSStringFromPoint(startLocation)); // MMLogInfo(@"end point: %@", NSStringFromPoint(endLocation)); - + if (Configuration.shared.adjustPopButtomOrigin) { // Since the pop button may cover selected text, we need to move it to the left. CGFloat horizontalOffset = 20; - + x = location.x; if (isDirectionRight) { x += horizontalOffset; } else { x -= (horizontalOffset + self.popButtonWindow.width); } - + y = location.y - self.offsetPoint.y; } - + NSPoint popLocation = CGPointMake(x, y); -// MMLogInfo(@"popLocation: %@", NSStringFromPoint(popLocation)); - + // MMLogInfo(@"popLocation: %@", NSStringFromPoint(popLocation)); + return popLocation; } @@ -564,13 +565,13 @@ - (CGPoint)getMiniWindowLocation { if (Configuration.shared.adjustPopButtomOrigin) { position.y = position.y - 8; } - + // If input query, just show mini window, then show window at last position. if (self.actionType == EZActionTypeInputQuery) { CGRect formerFrame = [EZLayoutManager.shared windowFrameWithType:EZWindowTypeMini]; position = [EZCoordinateUtils getFrameTopLeftPoint:formerFrame]; } - + return position; } @@ -584,24 +585,24 @@ - (CGPoint)getMouseLocation:(BOOL)offsetFlag { if (CGPointEqualToPoint(popButtonLocation, CGPointZero)) { return CGPointZero; } - + CGPoint mouseLocation = NSEvent.mouseLocation; CGPoint showingPosition = mouseLocation; - + if (offsetFlag) { CGFloat x = popButtonLocation.x + 5; // Move slightly to the right to avoid covering the cursor. - - + + // if pop button is left to selected text, we need to move showing mouse location to a bit right, to show query window properly. if (mouseLocation.x > popButtonLocation.x) { x = NSEvent.mouseLocation.x + 5; } - + CGFloat y = popButtonLocation.y + 0; - + showingPosition = CGPointMake(x, y); } - + return showingPosition; } @@ -635,29 +636,29 @@ - (CGPoint)getFixedWindowLocation { - (CGPoint)getFloatingWindowInRightSideOfScreenPoint:(NSWindow *)floatingWindow { CGPoint position = CGPointZero; - + NSScreen *targetScreen = self.screen; NSRect screenRect = [targetScreen visibleFrame]; - + CGFloat x = screenRect.origin.x + screenRect.size.width - floatingWindow.width; CGFloat y = screenRect.origin.y + screenRect.size.height; position = CGPointMake(x, y); - + return position; } /// Get the position of floatingWindow that make sure floatingWindow show in the center of self.screen. - (CGPoint)getFloatingWindowInCenterOfScreenPoint:(NSWindow *)floatingWindow { CGPoint position = CGPointZero; - + NSScreen *targetScreen = self.screen; NSRect screenRect = [targetScreen visibleFrame]; - + // top-left point CGFloat x = screenRect.origin.x + (screenRect.size.width - floatingWindow.width) / 2; CGFloat y = screenRect.origin.y + (screenRect.size.height - floatingWindow.height) / 2 + floatingWindow.height; position = CGPointMake(x, y); - + return position; } @@ -668,7 +669,7 @@ - (void)saveFrontmostApplication { if ([frontmostApplication.bundleIdentifier isEqualToString:identifier]) { return; } - + self.lastFrontmostApplication = frontmostApplication; } @@ -676,7 +677,7 @@ - (void)showMainWindowIfNeeded { BOOL showFlag = !Configuration.shared.hideMainWindow; NSApplicationActivationPolicy activationPolicy = showFlag ? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory; [NSApp setActivationPolicy:activationPolicy]; - + if (showFlag) { // If the main window does not exist, create it first, and show it center. if (!_mainWindow) { @@ -684,74 +685,99 @@ - (void)showMainWindowIfNeeded { } [self.mainWindow makeKeyAndOrderFront:nil]; [self.floatingWindowTypeArray insertObject:@(EZWindowTypeMain) atIndex:0]; - + // TODO: We should record main window showing position, like mini window. -// [self showFloatingWindowType:EZWindowTypeMain queryText:nil]; + // [self showFloatingWindowType:EZWindowTypeMain queryText:nil]; } } - (void)destroyMainWindow { [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; - + [self.floatingWindowTypeArray removeObject:@(EZWindowTypeMain)]; - + if ([EZMainQueryWindow isAlive]) { [EZMainQueryWindow destroySharedInstance]; _mainWindow = nil; } } +/** + Get a safe point for the pop button window. + + In order to avoid the pop button being hovered automatically when showing, we need to ensure that the pop button window is not too close to the screen edge. + + if safePoint.x is less than minX, we need to move it to minX + safeOffsetX + if safePoint.x is greater than maxX, we need to move it to maxX - safeOffsetX + */ +- (NSPoint)getSafePointForPopButtonWindow:(NSPoint)point { + CGFloat safeOffsetX = 50; + CGFloat minX = CGRectGetMinX(self.screen.visibleFrame); + CGFloat maxX = CGRectGetMaxX(self.screen.visibleFrame) - self.popButtonWindow.width; + + NSPoint safePoint = point; + + if (safePoint.x <= minX) { + safePoint.x = minX + safeOffsetX; + } + if (safePoint.x >= maxX) { + safePoint.x = maxX - safeOffsetX; + } + + return safePoint; +} + #pragma mark - Menu Actions, Global Shortcut - (void)selectTextTranslate { MMLogInfo(@"selectTextTranslate"); - + if (![self.eventMonitor isAccessibilityEnabled]) { MMLogWarn(@"App is not trusted"); return; } - + [self saveFrontmostApplication]; if (Snip.shared.isSnapshotting) { return; } - + EZWindowType windowType = Configuration.shared.shortcutSelectTranslateWindowType; MMLogInfo(@"selectTextTranslate windowType: %@", @(windowType)); self.eventMonitor.actionType = EZActionTypeShortcutQuery; [self.eventMonitor getSelectedText:^(NSString *_Nullable text) { self.actionType = self.eventMonitor.actionType; - + /** Clear query if text is nil and user don't want to keep the last result. - + !!!: text may be @"" when no selected text in Chrome, so we need to handle it. */ if (text.length == 0) { text = Configuration.shared.keepPrevResultWhenEmpty ? nil : @""; } self.selectedText = [text trim]; - + [self showFloatingWindowType:windowType queryText:self.selectedText]; }]; } - (void)snipTranslate { MMLogInfo(@"snipTranslate"); - + [self saveFrontmostApplication]; - + if (Snip.shared.isSnapshotting) { return; } - + // Close non-main floating window if not pinned. Fix https://github.com/tisfeng/Easydict/issues/126 [self closeFloatingWindowIfNotPinnedOrMain]; - + // Since ocr detect may be inaccurate, sometimes need to set sourceLanguage manually, so show Fixed window. EZWindowType windowType = Configuration.shared.shortcutSelectTranslateWindowType; EZBaseQueryWindow *window = [self windowWithType:windowType]; - + // Wait to close floating window if need. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [Snip.shared startWithCompletion:^(NSImage *_Nullable image) { @@ -759,9 +785,9 @@ - (void)snipTranslate { MMLogWarn(@"not get screenshot"); return; } - + MMLogInfo(@"get screenshot: %@", image); - + // 缓存最后一张图片,统一放到 MMLogs 文件夹,方便管理 static NSString *_imagePath = nil; static dispatch_once_t onceToken; @@ -771,7 +797,7 @@ - (void)snipTranslate { [[NSFileManager defaultManager] removeItemAtPath:_imagePath error:nil]; [image mm_writeToFileAsPNG:_imagePath]; MMLogInfo(@"已保存图片:%@", _imagePath); - + // Reset window height first, avoid being affected by previous window height. [window.queryViewController resetTableView:^{ self.actionType = EZActionTypeOCRQuery; @@ -784,24 +810,24 @@ - (void)snipTranslate { - (void)inputTranslate { MMLogInfo(@"inputTranslate"); - + [self saveFrontmostApplication]; if (Snip.shared.isSnapshotting) { return; } - + EZWindowType windowType = Configuration.shared.shortcutSelectTranslateWindowType; - + if (self.floatingWindowType == windowType && self.floatingWindow.isVisible) { [self closeFloatingWindow]; return; } - + NSString *queryText = nil; if ([Configuration.shared clearInput]) { queryText = @""; } - + self.actionType = EZActionTypeInputQuery; [self showFloatingWindowType:windowType queryText:queryText]; } @@ -809,35 +835,35 @@ - (void)inputTranslate { /// Show mini window at last positon. - (void)showMiniFloatingWindow { MMLogInfo(@"showMiniFloatingWindow"); - + EZWindowType windowType = Configuration.shared.mouseSelectTranslateWindowType; - + if (self.floatingWindowType == windowType && self.floatingWindow.isVisible) { [self closeFloatingWindow]; return; } - + self.actionType = EZActionTypeInputQuery; [self showFloatingWindowType:windowType queryText:nil]; } - (void)screenshotOCR { MMLogInfo(@"screenshotOCR"); - + [self saveFrontmostApplication]; - + if (Snip.shared.isSnapshotting) { return; } - + [Snip.shared startWithCompletion:^(NSImage *_Nullable image) { if (!image) { MMLogWarn(@"not get screenshot"); return; } - + MMLogInfo(@"get screenshot: %@", image); - + // 缓存最后一张图片,统一放到 MMLogs 文件夹,方便管理 static NSString *_imagePath = nil; static dispatch_once_t onceToken; @@ -847,7 +873,7 @@ - (void)screenshotOCR { [[NSFileManager defaultManager] removeItemAtPath:_imagePath error:nil]; [image mm_writeToFileAsPNG:_imagePath]; MMLogInfo(@"已保存图片:%@", _imagePath); - + [self.backgroundQueryViewController startOCRImage:image actionType:EZActionTypeScreenshotOCR]; }]; } @@ -858,7 +884,7 @@ - (void)rerty { if (Snip.shared.isSnapshotting) { return; } - + if ([[NSApplication sharedApplication] keyWindow] == self.floatingWindow) { // 执行重试 [self.floatingWindow.queryViewController retryQuery]; @@ -867,13 +893,13 @@ - (void)rerty { - (void)clearInput { MMLogInfo(@"Clear input"); - + [self.floatingWindow.queryViewController clearInput]; } - (void)clearAll { MMLogInfo(@"Clear All"); - + [self.floatingWindow.queryViewController clearAll]; } @@ -887,14 +913,14 @@ - (void)copyFirstTranslatedText { - (void)pin { MMLogInfo(@"Pin"); - + EZBaseQueryWindow *queryWindow = EZWindowManager.shared.floatingWindow; queryWindow.pin = !queryWindow.pin; } - (void)closeWindowOrExitSreenshot { MMLogInfo(@"Close window, or exit screenshot"); - + if (Snip.shared.isSnapshotting) { [Snip.shared stop]; } else { @@ -950,20 +976,20 @@ - (void)closeFloatingWindow:(EZWindowType)windowType { if (!floatingWindow || !floatingWindow.isVisible) { return; } - + MMLogInfo(@"close window type: %ld", windowType); - + floatingWindow.pin = NO; - + /// !!!: Close window may call window delegate method `windowDidResignKey:` /// And `windowDidResignKey:` will call `closeFloatingWindowIfNotPinned:` [floatingWindow close]; - + if (![self currentShowingSettingsWindow]) { - // Recover last app. + // Recover last app. [self activeLastFrontmostApplication]; } - + [self updateFloatingWindowType:windowType isShowing:NO]; } @@ -1001,17 +1027,17 @@ - (BOOL)isAppRunningWithBundleId:(NSString *)bundleID { - (void)logSelectedTextEvent { NSString *text = self.selectedText; - + if (!text) { return; } - + NSRunningApplication *application = self.eventMonitor.frontmostApplication; NSString *appName = application.localizedName ?: @""; NSString *bundleID = application.bundleIdentifier ?: @""; NSString *textLength = [EZLog textLengthRange:text]; NSString *triggerType = [EZEnumTypes stringValueOfTriggerType:self.eventMonitor.triggerType]; - + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:@{ @"actionType" : self.actionType, @"selectTextType" : self.eventMonitor.selectTextType, @@ -1020,14 +1046,14 @@ - (void)logSelectedTextEvent { @"appName" : appName, @"bundleID" : bundleID, }]; - + NSString *browserTabURLString = self.eventMonitor.browserTabURLString; if (browserTabURLString.length) { NSURL *tabURL = [NSURL URLWithString:browserTabURLString]; NSString *host = tabURL.host ?: browserTabURLString; dict[@"host"] = host; } - + [EZLog logEventWithName:@"getSelectedText" parameters:dict]; }