From 5e40c9f9adadabfd3898aba0df0b8e11bb741e3e Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Wed, 16 Jul 2025 13:55:19 +0800 Subject: [PATCH 01/14] tweak edge slide & touchHandler --- VoidLink/Input/AbsoluteTouchHandler.m | 2 +- VoidLink/Input/NativeTouchHandler.m | 7 ++++--- VoidLink/Input/PureNativeTouchHandler.m | 8 ++++---- VoidLink/Input/RelativeTouchHandler.m | 5 +++-- .../ViewControllers/CustomEdgeSlideGestureRecognizer.m | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/VoidLink/Input/AbsoluteTouchHandler.m b/VoidLink/Input/AbsoluteTouchHandler.m index 8b15aa05..2875bf55 100644 --- a/VoidLink/Input/AbsoluteTouchHandler.m +++ b/VoidLink/Input/AbsoluteTouchHandler.m @@ -47,7 +47,7 @@ self->streamView = view; // upper screen check - EDGE_TOLERANCE = 15.0; + EDGE_TOLERANCE = 10.0; slideGestureVerticalThreshold = CGRectGetHeight([[UIScreen mainScreen] bounds]) * 0.4; screenWidthWithThreshold = CGRectGetWidth([[UIScreen mainScreen] bounds]) - EDGE_TOLERANCE; self->touchPointSpawnedAtUpperScreenEdge = false; diff --git a/VoidLink/Input/NativeTouchHandler.m b/VoidLink/Input/NativeTouchHandler.m index 83b773f7..87833bfb 100644 --- a/VoidLink/Input/NativeTouchHandler.m +++ b/VoidLink/Input/NativeTouchHandler.m @@ -86,7 +86,7 @@ self->pointerObjDict = [NSMutableDictionary dictionary]; - EDGE_TOLERANCE = 15.0; + EDGE_TOLERANCE = 10.0; slideGestureVerticalThreshold = CGRectGetHeight([[UIScreen mainScreen] bounds]) * 0.4; screenWidthWithThreshold = CGRectGetWidth([[UIScreen mainScreen] bounds]) - EDGE_TOLERANCE; @@ -190,7 +190,7 @@ - (void)sendTouchEvent:(UITouch*)touch withTouchtype:(uint8_t)touchType{ - if(touchPointSpawnedAtUpperScreenEdge) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash! + if(touchPointSpawnedAtUpperScreenEdge && touchType != LI_TOUCH_EVENT_UP) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash CGPoint targetCoords; //NSLog(@"selecting coords: %d", touch.phase == UITouchPhaseMoved); @@ -264,7 +264,8 @@ [self removePointerId:touch]; //then remove pointerId if(self->activateCoordSelector) [self removePointerObjFromDict:touch]; } - if(self->touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count]) self->touchPointSpawnedAtUpperScreenEdge = false; //bugbugbugbugbugbugbugbug + //if(self->touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count]) + self->touchPointSpawnedAtUpperScreenEdge = false; }); else{ for (UITouch* touch in touches){ diff --git a/VoidLink/Input/PureNativeTouchHandler.m b/VoidLink/Input/PureNativeTouchHandler.m index fcda021c..cc34d5eb 100644 --- a/VoidLink/Input/PureNativeTouchHandler.m +++ b/VoidLink/Input/PureNativeTouchHandler.m @@ -85,7 +85,7 @@ self->pointerObjDict = [NSMutableDictionary dictionary]; - EDGE_TOLERANCE = 15.0; + EDGE_TOLERANCE = 10.0; slideGestureVerticalThreshold = CGRectGetHeight([[UIScreen mainScreen] bounds]) * 0.4; screenWidthWithThreshold = CGRectGetWidth([[UIScreen mainScreen] bounds]) - EDGE_TOLERANCE; @@ -189,8 +189,8 @@ - (void)sendTouchEvent:(UITouch*)touch withTouchtype:(uint8_t)touchType{ - if(touchPointSpawnedAtUpperScreenEdge) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash! - + if(touchPointSpawnedAtUpperScreenEdge && touchType != LI_TOUCH_EVENT_UP) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash + CGPoint targetCoords; //NSLog(@"selecting coords: %d", touch.phase == UITouchPhaseMoved); // NSLog(@"excluded count: %d", (uint32_t)[excludedPointerIds count]); @@ -253,7 +253,7 @@ [self removePointerId:touch]; //then remove pointerId if(self->activateCoordSelector) [self removePointerObjFromDict:touch]; } - if(self->touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count]) self->touchPointSpawnedAtUpperScreenEdge = false; + self->touchPointSpawnedAtUpperScreenEdge = false; }); else{ for (UITouch* touch in touches){ diff --git a/VoidLink/Input/RelativeTouchHandler.m b/VoidLink/Input/RelativeTouchHandler.m index f8cde591..0f67455d 100644 --- a/VoidLink/Input/RelativeTouchHandler.m +++ b/VoidLink/Input/RelativeTouchHandler.m @@ -63,7 +63,7 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; mousePointerTimestamp = 0; // upper screen check - EDGE_TOLERANCE = 15.0; + EDGE_TOLERANCE = 10.0; slideGestureVerticalThreshold = CGRectGetHeight([[UIScreen mainScreen] bounds]) * 0.4; screenWidthWithThreshold = CGRectGetWidth([[UIScreen mainScreen] bounds]) - EDGE_TOLERANCE; self->touchPointSpawnedAtUpperScreenEdge = false; @@ -238,8 +238,9 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; touchLockedForMouseMove = nil; mousePointerMoved = false; // need to reset this anyway [self resetAllPressedFlagsForOnScreenWidgetViews]; // reset all pressed flag for on-screen widget views after all fingers lifted from screen. - touchPointSpawnedAtUpperScreenEdge = false; } + + touchPointSpawnedAtUpperScreenEdge = false; } diff --git a/VoidLink/ViewControllers/CustomEdgeSlideGestureRecognizer.m b/VoidLink/ViewControllers/CustomEdgeSlideGestureRecognizer.m index 5224a0ea..858c0486 100644 --- a/VoidLink/ViewControllers/CustomEdgeSlideGestureRecognizer.m +++ b/VoidLink/ViewControllers/CustomEdgeSlideGestureRecognizer.m @@ -20,7 +20,7 @@ static CGFloat screenWidthInPoints; self = [super initWithTarget:target action:action]; screenWidthInPoints = CGRectGetWidth([UIApplication.sharedApplication.windows.firstObject.screen bounds]); // Get the screen's bounds (in points) _immediateTriggering = false; - _EDGE_TOLERANCE = 15.0f; + _EDGE_TOLERANCE = 10.0f; return self; } From 3dccec37395a72137a6361c42906fc80c916b67d Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Thu, 17 Jul 2025 02:05:05 +0800 Subject: [PATCH 02/14] appView update --- VoidLink/.DS_Store | Bin 10244 -> 10244 bytes .../icon-pc-app.imageset/Contents.json | 21 +++++ .../icon-pc-app.imageset/icon-pc-app.svg | 6 ++ .../play.circle.fill.imageset/Contents.json | 21 +++++ .../play.circle.fill.svg | 11 +++ VoidLink/UIAppView.m | 78 ++++++++++++------ .../ViewControllers/MainFrameViewController.m | 16 ++-- 7 files changed, 120 insertions(+), 33 deletions(-) create mode 100644 VoidLink/Images.xcassets/icon-pc-app.imageset/Contents.json create mode 100644 VoidLink/Images.xcassets/icon-pc-app.imageset/icon-pc-app.svg create mode 100644 VoidLink/Images.xcassets/play.circle.fill.imageset/Contents.json create mode 100644 VoidLink/Images.xcassets/play.circle.fill.imageset/play.circle.fill.svg diff --git a/VoidLink/.DS_Store b/VoidLink/.DS_Store index a37c9cc16bca35c4b436545ec137207e8e50e750..af91099b7496ebe7e7e723a5dc2273756f93404e 100644 GIT binary patch delta 51 zcmZn(XbG6$OBU^hRb+GZYs^~{rNh2?q5f{XHU^7GOe7#J8g?+{kx+03r+i)Hg= H5oTrptt$`h delta 37 tcmZn(XbG6$aFU^hRb>Si8+^~{@J3d`|KY|z`xuJDUxv%V-ZGXV6b3=IGP diff --git a/VoidLink/Images.xcassets/icon-pc-app.imageset/Contents.json b/VoidLink/Images.xcassets/icon-pc-app.imageset/Contents.json new file mode 100644 index 00000000..3a3cb79a --- /dev/null +++ b/VoidLink/Images.xcassets/icon-pc-app.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "icon-pc-app.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/VoidLink/Images.xcassets/icon-pc-app.imageset/icon-pc-app.svg b/VoidLink/Images.xcassets/icon-pc-app.imageset/icon-pc-app.svg new file mode 100644 index 00000000..04e56c40 --- /dev/null +++ b/VoidLink/Images.xcassets/icon-pc-app.imageset/icon-pc-app.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/VoidLink/Images.xcassets/play.circle.fill.imageset/Contents.json b/VoidLink/Images.xcassets/play.circle.fill.imageset/Contents.json new file mode 100644 index 00000000..53dcb23d --- /dev/null +++ b/VoidLink/Images.xcassets/play.circle.fill.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "play.circle.fill.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/VoidLink/Images.xcassets/play.circle.fill.imageset/play.circle.fill.svg b/VoidLink/Images.xcassets/play.circle.fill.imageset/play.circle.fill.svg new file mode 100644 index 00000000..c5320593 --- /dev/null +++ b/VoidLink/Images.xcassets/play.circle.fill.imageset/play.circle.fill.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/VoidLink/UIAppView.m b/VoidLink/UIAppView.m index ca1319d2..9d98d353 100644 --- a/VoidLink/UIAppView.m +++ b/VoidLink/UIAppView.m @@ -29,6 +29,9 @@ static UIImage* noImage; _callback = callback; _artCache = cache; + self.layer.cornerRadius = 16; + self.clipsToBounds = YES; + // Cache the NoAppImage ourselves to avoid // having to load it each time if (noImage == nil) { @@ -71,7 +74,7 @@ static UIImage* noImage; #else // Rasterizing the cell layer increases rendering performance by quite a bit // but we want it unrasterized for tvOS where it must be scaled. - self.layer.shouldRasterize = YES; + //self.layer.shouldRasterize = YES; self.layer.rasterizationScale = [UIScreen mainScreen].scale; if (@available(iOS 13.4.1, *)) { @@ -115,6 +118,10 @@ static UIImage* noImage; } #endif +- (bool)isCurrentApp{ + return [_app.id isEqualToString:_app.host.currentGame]; +} + - (void) updateAppImage { if (_appOverlay != nil) { @@ -138,6 +145,8 @@ static UIImage* noImage; [_artCache setObject:appImage forKey:_app]; } } + + // [_artCache setObject:appImage forKey:_app]; if (appImage != nil) { @@ -156,12 +165,15 @@ static UIImage* noImage; noAppImage = true; } - if ([_app.id isEqualToString:_app.host.currentGame]) { + + if([self isCurrentApp]) { // Only create the app overlay if needed if (@available(iOS 13.0, *)) { - UIImageView* playIcon = [[UIImageView alloc] initWithImage:[[UIImage systemImageNamed:@"play.circle"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]]; - playIcon.tintColor = [[UIColor systemTealColor] colorWithAlphaComponent:0.8]; + UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:23 weight:UIImageSymbolWeightBlack ]; + UIImageView* playIcon = [[UIImageView alloc] initWithImage:[[UIImage systemImageNamed:@"play.circle.fill" withConfiguration:config] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]]; + //playIcon.tintColor = [[ThemeManager widgetBackgroundColor] colorWithAlphaComponent:0.85]; + playIcon.tintColor = [[UIColor blackColor] colorWithAlphaComponent:0.55]; _appOverlay = playIcon; } else { @@ -170,23 +182,37 @@ static UIImage* noImage; } - _appOverlay.layer.shadowColor = [UIColor blackColor].CGColor; - _appOverlay.layer.shadowOffset = CGSizeMake(1, 1); - _appOverlay.layer.shadowOpacity = 1; - _appOverlay.layer.shadowRadius = 1.3; + //_appOverlay.layer.shadowColor = [UIColor blackColor].CGColor; + //_appOverlay.layer.shadowOffset = CGSizeMake(1, 1); + //_appOverlay.layer.shadowOpacity = 1; + //_appOverlay.layer.shadowRadius = 1.3; _appOverlay.contentMode = UIViewContentModeScaleAspectFit; } + else if(noAppImage){ + if (@available(iOS 13.0, *)) { + UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:70]; + UIImage* appIconImage = [[UIImage imageNamed:@"icon-pc-app"] imageWithConfiguration:config]; + UIImageView* appIcon = [[UIImageView alloc] initWithImage:[appIconImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]]; + + //layIcon.tintColor = [[ThemeManager widgetBackgroundColor] colorWithAlphaComponent:0.85]; + appIcon.tintColor = [[UIColor blackColor] colorWithAlphaComponent:0.55]; + + _appOverlay = appIcon; + } + } - if (noAppImage) { + if (true) { _appLabel = [[UILabel alloc] init]; - [_appLabel setTextColor:[[UIColor blackColor] colorWithAlphaComponent:0.75]]; + _appLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.55]; + [_appLabel setTextColor:[[UIColor whiteColor] colorWithAlphaComponent:1]]; //_appLabel.shadowColor = [UIColor blackColor]; [_appLabel setText:_app.name]; - [_appLabel setFont:[UIFont systemFontOfSize:20]]; + [_appLabel setFont:[UIFont systemFontOfSize:15]]; [_appLabel setBaselineAdjustment:UIBaselineAdjustmentAlignCenters]; [_appLabel setTextAlignment:NSTextAlignmentCenter]; [_appLabel setLineBreakMode:NSLineBreakByWordWrapping]; - [_appLabel setNumberOfLines:0]; + [_appLabel setNumberOfLines:2]; + _appLabel.lineBreakMode = NSLineBreakByTruncatingTail; } [self positionSubviews]; @@ -208,25 +234,26 @@ static UIImage* noImage; } - (void) positionSubviews { - CGFloat padding = 5.f; + // CGFloat padding = 28; + CGFloat verticalPadding = 10; CGSize frameSize = _appImage.frame.size; - CGPoint center = _appImage.center; + // CGPoint center = _appImage.center; + + [_appLabel setFrame:CGRectMake(0, frameSize.height-43, frameSize.width, 43)]; + //[_appLabel setFrame:CGRectMake(0, 0, 30, 12)]; + - if (_appLabel != nil) { if (_appOverlay != nil) { - _appOverlay.frame = CGRectMake(0, 0, frameSize.width / 3, frameSize.width / 3); - _appOverlay.center = CGPointMake(frameSize.width / 2, padding + _appOverlay.frame.size.height / 2); + _appOverlay.frame = [self isCurrentApp] ? CGRectMake(0, 0, frameSize.width / 2.39, frameSize.width / 2.39) : CGRectMake(0, 0, frameSize.width / 2, frameSize.width / 2); + _appOverlay.center = CGPointMake(frameSize.width/2, frameSize.height/2 - 2 * verticalPadding); - [_appLabel setFrame:CGRectMake(padding, _appOverlay.frame.size.height + padding, frameSize.width - 2 * padding, frameSize.height - _appOverlay.frame.size.height - 2 * padding)]; + //[_appLabel setFrame:CGRectMake(padding, _appOverlay.frame.size.height + padding, frameSize.width - 2 * padding, frameSize.height - _appOverlay.frame.size.height - 2 * padding)]; } - else { - [_appLabel setFrame:CGRectMake(padding, padding, frameSize.width - 2 * padding, frameSize.height - 2 * padding)]; - } - } + /* else if (_appOverlay != nil) { _appOverlay.frame = CGRectMake(0, 0, frameSize.width / 2, frameSize.width / 2); _appOverlay.center = center; - } + }*/ } - (void) updateLoop { @@ -244,8 +271,9 @@ static UIImage* noImage; // Show no shadow for hidden apps. Because we adjust the opacity of the // cells for hidden apps, it makes them look bad when the shadow draws // through the app tile. - self.superview.layer.shadowOpacity = _app.hidden ? 0.0f : 0.5f; - + // self.superview.layer.shadowOpacity = _app.hidden ? 0.0f : 0.5f; + self.superview.layer.shadowOpacity = 0; + // Update opacity if neccessary [self setAlpha:_app.hidden ? 0.4 : 1.0]; diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index a8878111..669b973f 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -2001,7 +2001,7 @@ static NSMutableSet* hostList; if (appView.bounds.size.width > 10.0) { CGFloat scale = cell.bounds.size.width / appView.bounds.size.width; [appView setCenter:CGPointMake(appView.bounds.size.width / 2 * scale, appView.bounds.size.height / 2 * scale)]; - appView.transform = CGAffineTransformMakeScale(scale, scale); + appView.transform = CGAffineTransformMakeScale(scale, scale); // view resize } [cell.subviews.firstObject removeFromSuperview]; // Remove a view that was previously added @@ -2009,15 +2009,15 @@ static NSMutableSet* hostList; // [self.settingsButton setEnabled:![self isIPhonePortrait]]; // update settings button after host is clicked & appview loaded // Shadow opacity is controlled inside UIAppView based on whether the app // is hidden or not during the update cycle. - UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:cell.bounds]; - cell.layer.masksToBounds = NO; - cell.layer.shadowColor = [UIColor blackColor].CGColor; - cell.layer.shadowOffset = CGSizeMake(1.0f, 5.0f); - cell.layer.shadowPath = shadowPath.CGPath; + //UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:cell.bounds]; + //cell.layer.masksToBounds = YES; + //cell.layer.shadowColor = [UIColor blackColor].CGColor; + //cell.layer.shadowOffset = CGSizeMake(1.0f, 5.0f); + //cell.layer.shadowPath = shadowPath.CGPath; #if !TARGET_OS_TV - cell.layer.borderWidth = 1; - cell.layer.borderColor = [[UIColor colorWithRed:0 green:0 blue:0 alpha:0.3f] CGColor]; + //cell.layer.borderWidth = 1; + //cell.layer.borderColor = [[UIColor colorWithRed:0 green:0 blue:0 alpha:0.3f] CGColor]; cell.exclusiveTouch = YES; #endif From 98f73928fabc58179fb2e93f6c4df801de208465 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Thu, 17 Jul 2025 16:34:49 +0800 Subject: [PATCH 03/14] zh-hant, zh-hk localization --- VoidLink/Localization/Localizable.xcstrings | 715 ++++++++++---------- 1 file changed, 358 insertions(+), 357 deletions(-) diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index ab4b6d92..36b26495 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -13,13 +13,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "\n1. 展开设置菜单: 从%@边缘滑动%d%%屏幕宽度\n\n2. 打开命令工具: 从%@边缘滑动%d%%屏幕宽度\n\n3. 返回串流 / 收起设置菜单: 从左边缘滑动\n\n4. 从40%%上半边缘滑动避免触控事件影响PC端" + "value" : "\n1. 展開設定選單:從%@邊緣滑動%d%%的螢幕寬度\n\n2. 開啟指令工具:從%@邊緣滑動%d%%的螢幕寬度\n\n3. 從設定選單返回串流:從左側邊緣滑動\n\n4. 從螢幕邊緣上方40%%處滑動以避免將觸控事件傳送到電腦端" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "\n1. 展开设置菜单: 从%@边缘滑动%d%%屏幕宽度\n\n2. 打开命令工具: 从%@边缘滑动%d%%屏幕宽度\n\n3. 返回串流 / 收起设置菜单: 从左边缘滑动\n\n4. 从40%%上半边缘滑动避免触控事件影响PC端" + "value" : "\n1. 展開設定選單:從%@邊緣滑動%d%%的螢幕寬度\n\n2. 開啟指令工具:從%@邊緣滑動%d%%的螢幕寬度\n\n3. 從設定選單返回串流:從左側邊緣滑動\n\n4. 從螢幕邊緣上方40%%處滑動以避免將觸控事件傳送到電腦端" } } } @@ -36,13 +36,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "\n处理延迟 最小/最大/平均: %.1f/%.1f/%.1f ms" + "value" : "\n主機處理延遲 最小/最大/平均:%.1f/%.1f/%.1f ms" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "\n处理延迟 最小/最大/平均: %.1f/%.1f/%.1f ms" + "value" : "\n主機處理延遲 最小/最大/平均:%.1f/%.1f/%.1f ms" } } } @@ -59,13 +59,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : " 斷開 " + "value" : " 中斷連線 " } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : " 斷開 " + "value" : " 中斷連線 " } } } @@ -128,13 +128,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : " 启动桌面" + "value" : " 啟動" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : " 启动桌面" + "value" : " 啟動" } } } @@ -151,13 +151,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : " 输入PIN码配对" + "value" : " 使用PIN碼配對" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : " 输入PIN码配对" + "value" : " 使用PIN碼配對" } } } @@ -174,13 +174,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : " 当前布局: %@ 当前组件: %@" + "value" : " 版面配置:%@ 小工具:%@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : " 当前布局: %@ 当前组件: %@" + "value" : " 版面配置:%@ 小工具:%@" } } } @@ -197,13 +197,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : " 唤醒" + "value" : " 網路喚醒" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : " 唤醒" + "value" : " 網路喚醒" } } } @@ -220,13 +220,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : " 添加主机" + "value" : " 新增主機" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : " 添加主机" + "value" : " 新增主機" } } } @@ -249,13 +249,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "needs_review", - "value" : "\n\n设备的网络连接似乎正在屏蔽Moonlight. 可能无法在此网络中串流." + "value" : "\n\n您裝置的遠端工作階段連線被阻擋。在此網路上連線時,串流可能無法運作。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "\n\n设备的网络连接似乎正在屏蔽远程会话. 可能无法在此网络中串流." + "value" : "\n\n您裝置的遠端工作階段連線被阻擋。在此網路上連線時,串流可能無法運作。" } } } @@ -272,13 +272,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "[ 打开本地软键盘 ]" + "value" : "[ 開啟軟體鍵盤 ]" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "[ 打开本地软键盘 ]" + "value" : "[ 開啟軟體鍵盤 ]" } } } @@ -295,13 +295,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "[ 进入画中画模式 ]" + "value" : "[ 進入子母畫面模式 ]" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "[ 进入画中画模式 ]" + "value" : "[ 進入子母畫面模式 ]" } } } @@ -318,13 +318,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "[ 打开屏显控件工具 ]" + "value" : "[ 開啟螢幕小工具 ]" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "[ 打开屏显控件工具 ]" + "value" : "[ 開啟螢幕小工具 ]" } } } @@ -341,13 +341,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "[ 切换屏显控件布局 ]" + "value" : "[ 切換螢幕小工具版面配置 ]" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "[ 切换屏显控件布局 ]" + "value" : "[ 切換螢幕小工具版面配置 ]" } } } @@ -364,13 +364,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "%@ 正在运行" + "value" : "%@ 目前正在執行" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "%@ 正在运行" + "value" : "%@ 目前正在執行" } } } @@ -387,13 +387,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "%d指轻按手势" + "value" : "%d指輕點" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "%d指轻按手势" + "value" : "%d指輕點" } } } @@ -410,13 +410,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "%d%%屏幕宽度" + "value" : "%d%%螢幕寬度" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "%d%%屏幕宽度" + "value" : "%d%%螢幕寬度" } } } @@ -433,13 +433,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "%u ms (抖动: %u ms)" + "value" : "%u ms (變異數:%u ms)" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "%u ms (抖动: %u ms)" + "value" : "%u ms (變異數:%u ms)" } } } @@ -456,13 +456,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "关于" + "value" : "關於" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "关于" + "value" : "關於" } } } @@ -479,13 +479,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "添加" + "value" : "新增" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "添加" + "value" : "新增" } } } @@ -502,13 +502,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "新增/复制" + "value" : "新增/複製" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "新增/复制" + "value" : "新增/複製" } } } @@ -531,13 +531,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "添加主机" + "value" : "新增主機" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "添加主机" + "value" : "新增主機" } } } @@ -554,13 +554,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "手动添加远程主机" + "value" : "手動新增主機" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "手动添加远程主机" + "value" : "手動新增主機" } } } @@ -577,13 +577,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "收藏" + "value" : "新增至我的最愛" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "收藏" + "value" : "新增至我的最愛" } } } @@ -600,13 +600,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "别名 (允许留空)" + "value" : "別名 (可選)" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "别名 (允许留空)" + "value" : "別名 (可選)" } } } @@ -623,13 +623,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "别名标签 (可留空)" + "value" : "別名標籤 (可選)" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "别名标签 (可留空)" + "value" : "別名標籤 (可選)" } } } @@ -646,13 +646,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "全部设置" + "value" : "所有設定" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "全部设置" + "value" : "所有設定" } } } @@ -669,13 +669,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "控件透明度" + "value" : "透明度" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "控件透明度" + "value" : "透明度" } } } @@ -692,13 +692,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "控件透明度: %.2f" + "value" : "透明度:%.2f" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "控件透明度: %.2f" + "value" : "透明度:%.2f" } } } @@ -715,13 +715,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "选择应用" + "value" : "應用程式" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "选择应用" + "value" : "應用程式" } } } @@ -738,13 +738,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "音频" + "value" : "音訊" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "音频" + "value" : "音訊" } } } @@ -784,13 +784,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "小球: 假定的触点 十字: 指示器偏离位置" + "value" : "球:假設的觸控點 十字:指示器位置" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "小球: 假定的触点 十字: 指示器偏离位置" + "value" : "球:假設的觸控點 十字:指示器位置" } } } @@ -807,13 +807,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "串流码率: %.1f Mbps" + "value" : "位元率:%.1f Mbps" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "串流码率: %.1f Mbps" + "value" : "位元率:%.1f Mbps" } } } @@ -830,13 +830,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "串流码率: %.1f Mbps - 码率偏高, 谨慎使用!" + "value" : "位元率:%.1f Mbps - 請謹慎使用高位元率!" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "串流码率: %.1f Mbps - 码率偏高, 谨慎使用!" + "value" : "位元率:%.1f Mbps - 請謹慎使用高位元率!" } } } @@ -859,13 +859,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "调高码率, 尤其是超过50Mbps时, 请确保具备相应网络条件." + "value" : "僅在您的網路有能力時才增加位元率,尤其是在超過 50 Mbps 時要特別小心。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "调高码率, 尤其是超过50Mbps时, 请确保具备相应网络条件." + "value" : "僅在您的網路有能力時才增加位元率,尤其是在超過 50 Mbps 時要特別小心。" } } } @@ -882,13 +882,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "边界宽度" + "value" : "邊框寬度" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "边界宽度" + "value" : "邊框寬度" } } } @@ -905,13 +905,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "边界宽度: %.2f" + "value" : "邊框寬度:%.2f" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "边界宽度: %.2f" + "value" : "邊框寬度:%.2f" } } } @@ -951,13 +951,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "请检查输入的命令和参数" + "value" : "請檢查指令和參數。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "请检查输入的命令和参数" + "value" : "請檢查指令和參數。" } } } @@ -974,13 +974,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "收起" + "value" : "收合" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "收起" + "value" : "收合" } } } @@ -997,13 +997,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "命令" + "value" : "指令" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "命令" + "value" : "指令" } } } @@ -1020,13 +1020,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "正在连接" + "value" : "正在連線" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "正在连接" + "value" : "正在連線" } } } @@ -1043,13 +1043,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "连接错误" + "value" : "連線錯誤" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "连接错误" + "value" : "連線錯誤" } } } @@ -1066,13 +1066,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "连接失败" + "value" : "連線失敗" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "连接失败" + "value" : "連線失敗" } } } @@ -1089,13 +1089,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "连接帮助" + "value" : "連線說明" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "连接失败" + "value" : "連線說明" } } } @@ -1112,13 +1112,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "连接已中断" + "value" : "連線已中斷" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "连接已中断" + "value" : "連線已中斷" } } } @@ -1135,13 +1135,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "连接已终止" + "value" : "連線已終止" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "连接已终止" + "value" : "連線已終止" } } } @@ -1164,13 +1164,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "\n\n检查主机端防火墙以及端口%s的转发规则" + "value" : "\n\n檢查您的防火牆和以下連接埠的連接埠轉發規則:\n%s" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "\n\n检查主机端防火墙以及端口%s的转发规则" + "value" : "\n\n檢查您的防火牆和以下連接埠的連接埠轉發規則:\n%s" } } } @@ -1187,13 +1187,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "新增" + "value" : "建立新的" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "新增" + "value" : "建立新的" } } } @@ -1210,13 +1210,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "当前布局已修改" + "value" : "目前版面配置已成功更新" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "当前布局已修改" + "value" : "目前版面配置已成功更新" } } } @@ -1233,13 +1233,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "已设定为自定义分辨率" + "value" : "已選取自訂解析度" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "已设定为自定义分辨率" + "value" : "已選取自訂解析度" } } } @@ -1256,13 +1256,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "您可能需要在主机端的显卡控制面板手动添加自定义分辨率" + "value" : "GeForce Experience 未正式支援自訂解析度,因此不會設定您的主機顯示解析度。您需要在遊戲中手動設定。\n\n您的用戶端或主機電腦不支援的解析度可能會導致串流錯誤。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "您可能需要在主机端的显卡控制面板手动添加自定义分辨率" + "value" : "GeForce Experience 未正式支援自訂解析度,因此不會設定您的主機顯示解析度。您需要在遊戲中手動設定。\n\n您的用戶端或主機電腦不支援的解析度可能會導致串流錯誤。" } } } @@ -1279,13 +1279,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "减速率: %.3f " + "value" : "減速率:%.3f " } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "减速率: %.3f " + "value" : "減速率:%.3f " } } } @@ -1302,13 +1302,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "删除" + "value" : "刪除" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "删除" + "value" : "刪除" } } } @@ -1325,13 +1325,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "按键删除" + "value" : "在此處刪除按鈕" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "按键删除" + "value" : "在此處刪除按鈕" } } } @@ -1348,13 +1348,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "检测中…" + "value" : "正在偵測..." } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "检测中…" + "value" : "正在偵測..." } } } @@ -1371,13 +1371,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "断开" + "value" : "中斷連線" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "断开" + "value" : "中斷連線" } } } @@ -1417,13 +1417,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "将按键拖动到这个垃圾桶即可删除" + "value" : "將按鈕拖放到此垃圾桶以將其從介面中移除" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "将按键拖动到这个垃圾桶即可删除" + "value" : "將按鈕拖放到此垃圾桶以將其從介面中移除" } } } @@ -1440,13 +1440,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "编辑" + "value" : "編輯" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "编辑" + "value" : "編輯" } } } @@ -1463,13 +1463,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "编辑所选控件" + "value" : "編輯選取的小工具" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "编辑所选控件" + "value" : "編輯選取的小工具" } } } @@ -1486,13 +1486,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "输入新命令和对应的别名" + "value" : "輸入新指令與別名" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "输入新命令和对应的别名" + "value" : "輸入新指令與別名" } } } @@ -1509,13 +1509,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "输入自定义分辨率" + "value" : "輸入自訂解析度" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "输入自定义分辨率" + "value" : "輸入自訂解析度" } } } @@ -1532,13 +1532,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "输入主机端IP地址" + "value" : "輸入IP位址以手動新增主機" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "输入主机端IP地址" + "value" : "輸入IP位址以手動新增主機" } } } @@ -1555,13 +1555,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "输入控件命令和别名标签" + "value" : "輸入指令與別名標籤" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "输入控件命令和别名标签" + "value" : "輸入指令與別名標籤" } } } @@ -1578,13 +1578,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "将当前布局另存为" + "value" : "輸入您要儲存此版面配置的名稱" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "将当前布局另存为" + "value" : "輸入您要儲存此版面配置的名稱" } } } @@ -1607,13 +1607,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "\n请在主机端输入PIN码: %@\n\n如果主机运行的是Sunshine, 请在网页控制台输入PIN码" + "value" : "\n在主機上輸入以下 PIN 碼:%@\n\n如果您的主機電腦正在執行 Sunshine,請導覽至 Sunshine Web UI 以輸入 PIN" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "\n请在主机端输入PIN码: %@\n\n如果主机运行的是Sunshine, 请在网页控制台输入PIN码" + "value" : "\n在主機上輸入以下 PIN 碼:%@\n\n如果您的主機電腦正在執行 Sunshine,請導覽至 Sunshine Web UI 以輸入 PIN" } } } @@ -1630,13 +1630,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "退出" + "value" : "離開" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "退出" + "value" : "離開" } } } @@ -1653,13 +1653,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "实验性功能" + "value" : "實驗性" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "实验性功能" + "value" : "實驗性" } } } @@ -1676,13 +1676,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "布局导入失败" + "value" : "匯入版面配置失敗" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "布局导入失败" + "value" : "匯入版面配置失敗" } } } @@ -1699,13 +1699,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "退出应用失败. 如果此应用是由其他串流设备启动的, 请用相应的设备退出此应用" + "value" : "退出應用程式失敗。如果此應用程式是由其他裝置啟動的,您需要從該裝置退出。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "退出应用失败. 如果此应用是由其他串流设备启动的, 请用相应的设备退出此应用" + "value" : "退出應用程式失敗。如果此應用程式是由其他裝置啟動的,您需要從該裝置退出。" } } } @@ -1722,13 +1722,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "解析主机域名失败" + "value" : "解析主機失敗。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "解析主机域名失败" + "value" : "解析主機失敗。" } } } @@ -1745,13 +1745,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "收藏夹" + "value" : "我的最愛設定" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "收藏夹" + "value" : "我的最愛設定" } } } @@ -1768,13 +1768,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "首次串流提示" + "value" : "首次啟動提示" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "首次串流提示" + "value" : "首次啟動提示" } } } @@ -1791,13 +1791,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "帧率: %5.2f 网络丢帧: %.2f%% 网络延迟: %@" + "value" : "FPS:%5.2f 網路掉幀:%.2f%% 網路延遲:%@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "帧率: %5.2f 网络丢帧: %.2f%% 网络延迟: %@" + "value" : "FPS:%5.2f 網路掉幀:%.2f%% 網路延遲:%@" } } } @@ -1814,13 +1814,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "手势" + "value" : "手勢" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "手势" + "value" : "手勢" } } } @@ -1837,13 +1837,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "了解!" + "value" : "知道了!" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "了解!" + "value" : "知道了!" } } } @@ -1860,13 +1860,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "了解! (%ld)" + "value" : "知道了!(%ld)" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "了解! (%ld)" + "value" : "知道了!(%ld)" } } } @@ -1883,13 +1883,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "控件高度" + "value" : "高度" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "控件高度" + "value" : "高度" } } } @@ -1906,13 +1906,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "控件高度: %.2f" + "value" : "高度:%.2f" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "控件高度: %.2f" + "value" : "高度:%.2f" } } } @@ -1929,13 +1929,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "隐藏应用" + "value" : "隱藏應用程式" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "隐藏应用" + "value" : "隱藏應用程式" } } } @@ -1952,13 +1952,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "主机信息已更新" + "value" : "主機資訊已更新" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "主机信息已更新" + "value" : "主機資訊已更新" } } } @@ -1975,13 +1975,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "未知主机MAC地址, 无法发送WOL包" + "value" : "主機 MAC 未知,無法傳送 WOL 封包" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "未知主机MAC地址, 无法发送WOL包" + "value" : "主機 MAC 未知,無法傳送 WOL 封包" } } } @@ -1998,13 +1998,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "远程主机" + "value" : "主機" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "远程主机" + "value" : "主機" } } } @@ -2021,13 +2021,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "指示器偏离: %.0f" + "value" : "指示器偏移:%.0f" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "指示器偏离: %.0f" + "value" : "指示器偏移:%.0f" } } } @@ -2044,13 +2044,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "触点移动事件间隔: %d μs" + "value" : "多點觸控移動事件間隔:%d μs" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "触点移动事件间隔: %d μs" + "value" : "多點觸控移動事件間隔:%d μs" } } } @@ -2067,13 +2067,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "无效输入" + "value" : "無效輸入" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "无效输入" + "value" : "無效輸入" } } } @@ -2090,13 +2090,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "启动桌面" + "value" : "啟動" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "启动桌面" + "value" : "啟動" } } } @@ -2113,13 +2113,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "启动应用" + "value" : "啟動應用程式" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "启动应用" + "value" : "啟動應用程式" } } } @@ -2136,13 +2136,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "默认布局无法被修改" + "value" : "無法變更預設版面配置" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "默认布局无法被修改" + "value" : "無法變更預設版面配置" } } } @@ -2182,13 +2182,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "本地软键盘切换手势已禁用" + "value" : "本機鍵盤切換已停用" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "本地软键盘切换手势已禁用" + "value" : "本機鍵盤切換已停用" } } } @@ -2211,13 +2211,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "无法连接远程主机\n\n如果主机运行的是Geforce Experience, 确认SHIELD已启用.\n\n如果主机运行的是Sushine, 确认服务正常运行. 如果使用非默认端口, 请添加到地址中." + "value" : "無法連線到主機。\n\n如果您使用 GeForce Experience 進行託管,請確保您已在 SHIELD 標籤上啟用切換。\n\n如果您使用 Sunshine 進行託管,請確保其正常執行。如果您使用的是非預設連接埠,則需要在此處包含該連接埠。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "无法连接远程主机\n\n如果主机运行的是Geforce Experience, 确认SHIELD已启用.\n\n如果主机运行的是Sushine, 确认服务正常运行. 如果使用非默认端口, 请添加到地址中." + "value" : "無法連線到主機。\n\n如果您使用 GeForce Experience 進行託管,請確保您已在 SHIELD 標籤上啟用切換。\n\n如果您使用 Sunshine 進行託管,請確保其正常執行。如果您使用的是非預設連接埠,則需要在此處包含該連接埠。" } } } @@ -2234,13 +2234,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "手柄摇杆最小偏移量 (0~32766)" + "value" : "搖桿最小偏移量 (0~32766)" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "手柄摇杆最小偏移量 (0~32766)" + "value" : "搖桿最小偏移量 (0~32766)" } } } @@ -2263,13 +2263,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "needs_review", - "value" : "无法执行网络测试. 请检查网络连接后重试." + "value" : "由於無法連線到任何連線測試伺服器,因此無法執行網路測試。請檢查您的網際網路連線或稍後再試。" } }, "zh-HK" : { "stringUnit" : { "state" : "needs_review", - "value" : "无法执行网络测试. 请检查网络连接后重试." + "value" : "由於無法連線到任何連線測試伺服器,因此無法執行網路測試。請檢查您的網際網路連線或稍後再試。" } } } @@ -2309,13 +2309,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "鼠标指针灵敏度: %d%%" + "value" : "滑鼠指標速度:%d%%" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "鼠标指针灵敏度: %d%%" + "value" : "滑鼠指標速度:%d%%" } } } @@ -2332,13 +2332,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "名称" + "value" : "名稱" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "名称" + "value" : "名稱" } } } @@ -2361,13 +2361,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "needs_review", - "value" : "设备的网络连接似乎正在阻塞Moonlight. 可能无法在此网络中串流.\n\n以下网络端口被阻塞:\n%s" + "value" : "您目前的網路連線似乎正在阻擋遠端工作階段。在此網路上連線時,串流可能無法運作。\n\n下列網路連接埠被封鎖:\n%s" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "设备的网络连接似乎正在阻塞远程会话. 可能无法在此网络中串流.\n\n以下网络端口被阻塞:\n%s" + "value" : "您目前的網路連線似乎正在阻擋遠端工作階段。在此網路上連線時,串流可能無法運作。\n\n下列網路連接埠被封鎖:\n%s" } } } @@ -2390,13 +2390,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "needs_review", - "value" : "网络测试成功. 如果仍然无法连接远程主机, 请检查主机端防火墙设置." + "value" : "此網路似乎未阻擋遠端工作階段。如果您仍然無法連線,請檢查您電腦的防火牆設定。" } }, "zh-HK" : { "stringUnit" : { "state" : "needs_review", - "value" : "网络测试成功. 如果仍然无法连接远程主机, 请检查主机端防火墙设置." + "value" : "此網路似乎未阻擋遠端工作階段。如果您仍然無法連線,請檢查您電腦的防火牆設定。" } } } @@ -2413,13 +2413,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "网络错误" + "value" : "網路錯誤" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "网络错误" + "value" : "網路錯誤" } } } @@ -2436,13 +2436,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "网络测试已完成" + "value" : "網路測試完成" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "网络测试已完成" + "value" : "網路測試完成" } } } @@ -2459,13 +2459,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "新建命令" + "value" : "新指令" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "新建命令" + "value" : "新指令" } } } @@ -2482,13 +2482,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "新建屏显控件" + "value" : "新螢幕小工具" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "新建屏显控件" + "value" : "新螢幕小工具" } } } @@ -2505,13 +2505,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "无法从主机接收视频帧" + "value" : "未從主機收到視訊。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "无法从主机接收视频帧" + "value" : "未從主機收到視訊。" } } } @@ -2528,13 +2528,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "无修改可撤消" + "value" : "沒有可復原的內容" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "无修改可撤消" + "value" : "沒有可復原的內容" } } } @@ -2551,13 +2551,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "NVIDIA GAMESTREAM 服务终止" + "value" : "NVIDIA GameStream 服務終止" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "NVIDIA GAMESTREAM 服务终止" + "value" : "NVIDIA GameStream 服務終止" } } } @@ -2574,13 +2574,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "离线" + "value" : "離線" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "离线" + "value" : "離線" } } } @@ -2597,13 +2597,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "确认" + "value" : "確定" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "确认" + "value" : "確定" } } } @@ -2620,13 +2620,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "确认" + "value" : "確定" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "确认" + "value" : "確定" } } } @@ -2643,13 +2643,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "屏显虚拟手柄与控件" + "value" : "螢幕控制器與小工具" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "屏显虚拟手柄与控件" + "value" : "螢幕控制器與小工具" } } } @@ -2666,13 +2666,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "在线" + "value" : "線上" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "在线" + "value" : "線上" } } } @@ -2689,13 +2689,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "在线 - 未配对" + "value" : "線上 - 未配對" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "在线 - 未配对" + "value" : "線上 - 未配對" } } } @@ -2712,13 +2712,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "在线 - 已配对" + "value" : "線上 - 已配對" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "在线 - 已配对" + "value" : "線上 - 已配對" } } } @@ -2735,13 +2735,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "在串流视图中修改布局:\n从屏幕%@边缘向内滑动.\n或轻按%d根手指, 手势需要的手指数量:" + "value" : "在串流中開啟小工具的方式:\n從螢幕%@邊緣滑動以開啟指令工具。\n或在串流畫面上用%d指輕點,所需的手指數量:" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "在串流视图中修改布局:\n从屏幕%@边缘向内滑动.\n或轻按%d根手指, 手势需要的手指数量:" + "value" : "在串流中開啟小工具的方式:\n從螢幕%@邊緣滑動以開啟指令工具。\n或在串流畫面上用%d指輕點,所需的手指數量:" } } } @@ -2758,13 +2758,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "选项" + "value" : "選項" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "选项" + "value" : "選項" } } } @@ -2804,13 +2804,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "正在配对" + "value" : "正在配對" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "正在配对" + "value" : "正在配對" } } } @@ -2827,13 +2827,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "配对失败" + "value" : "配對失敗" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "配对失败" + "value" : "配對失敗" } } } @@ -2850,13 +2850,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "外设" + "value" : "周邊設備" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "外设" + "value" : "周邊設備" } } } @@ -2873,13 +2873,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "布局已导入" + "value" : "已匯入版面配置" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "布局已导入" + "value" : "已匯入版面配置" } } } @@ -2902,13 +2902,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "灵敏度分割线, 用于划分多点触控游戏(原神/崩坏星穹铁道)中触点移速的透传区域和调整区域. 详见在线文档." + "value" : "靈敏度分割線:用於在多點觸控遊戲(例如《原神》、《崩壞:星穹鐵道》)中分隔觸控點移動速度的傳遞區和調整區。詳情請參閱線上文件。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "灵敏度分割线, 用于划分多点触控游戏(原神/崩坏星穹铁道)中触点移速的透传区域和调整区域. 详见在线文档." + "value" : "靈敏度分割線:用於在多點觸控遊戲(例如《原神》、《崩壞:星穹鐵道》)中分隔觸控點移動速度的傳遞區和調整區。詳情請參閱線上文件。" } } } @@ -2931,13 +2931,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "needs_review", - "value" : "触点灵敏度, 用于调节右半屏触点的移速, 进而实现(原神/崩坏星穹铁道)游戏内视角转动的灵敏度调节.详见在线文档." + "value" : "觸控靈敏度:調整螢幕右半邊觸控點的移動速度,讓您可以控制遊戲內攝影機的靈敏度(適用於《原神》和《崩壞:星穹鐵道》)。詳情請參閱線上文件。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "触点灵敏度, 用于调节右半屏触点的移速, 进而实现(原神/崩坏星穹铁道)游戏内视角转动的灵敏度调节.详见在线文档." + "value" : "觸控靈敏度:調整螢幕右半邊觸控點的移動速度,讓您可以控制遊戲內攝影機的靈敏度(適用於《原神》和《崩壞:星穹鐵道》)。詳情請參閱線上文件。" } } } @@ -2954,13 +2954,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "网络状态较差" + "value" : "與電腦的連線品質不佳" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "网络状态较差" + "value" : "與電腦的連線品質不佳" } } } @@ -2977,13 +2977,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "当前布局已另存为: %@" + "value" : "已從目前版面配置複製版面配置 %@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "当前布局已另存为: %@" + "value" : "已從目前版面配置複製版面配置 %@" } } } @@ -3000,13 +3000,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "默认布局无法被覆盖保存" + "value" : "無法覆寫預設版面配置" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "默认布局无法被覆盖保存" + "value" : "無法覆寫預設版面配置" } } } @@ -3023,13 +3023,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "布局名称已存" + "value" : "版面配置名稱已存在" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "布局名称已存" + "value" : "版面配置名稱已存在" } } } @@ -3046,13 +3046,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "布局名称不能为空!" + "value" : "版面配置名稱不可為空!" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "布局名称不能为空!" + "value" : "版面配置名稱不可為空!" } } } @@ -3069,13 +3069,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "当前布局: %@" + "value" : "版面配置:%@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "当前布局: %@" + "value" : "版面配置:%@" } } } @@ -3092,13 +3092,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "退出应用" + "value" : "退出應用程式" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "退出应用" + "value" : "退出應用程式" } } } @@ -3115,13 +3115,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "退出运行中的应用" + "value" : "退出執行中的應用程式" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "退出运行中的应用" + "value" : "退出執行中的應用程式" } } } @@ -3138,13 +3138,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "退出运行中的应用, 并运行此应用" + "value" : "退出執行中的應用程式並啟動" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "退出运行中的应用, 并运行此应用" + "value" : "退出執行中的應用程式並啟動" } } } @@ -3161,13 +3161,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "退出应用失败" + "value" : "退出應用程式失敗" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "退出应用失败" + "value" : "退出應用程式失敗" } } } @@ -3184,13 +3184,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "正在退出应用: %@" + "value" : "正在退出應用程式:%@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "正在退出应用: %@" + "value" : "正在退出應用程式:%@" } } } @@ -3207,13 +3207,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "阅读控件添加说明" + "value" : "閱讀小工具說明" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "阅读控件添加说明" + "value" : "閱讀小工具說明" } } } @@ -3230,13 +3230,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "串流中修改布局" + "value" : "在串流中修改" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "串流中修改布局" + "value" : "在串流中修改" } } } @@ -3276,13 +3276,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "移除主机" + "value" : "移除主機" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "移除主机" + "value" : "移除主機" } } } @@ -3299,13 +3299,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "移除设置项" + "value" : "移除項目" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "移除设置项" + "value" : "移除項目" } } } @@ -3322,13 +3322,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "恢复应用" + "value" : "恢復應用程式" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "恢复应用" + "value" : "恢復應用程式" } } } @@ -3345,13 +3345,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "恢复运行中的应用" + "value" : "恢復執行中的應用程式" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "恢复运行中的应用" + "value" : "恢復執行中的應用程式" } } } @@ -3391,13 +3391,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "保存" + "value" : "儲存" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "保存" + "value" : "儲存" } } } @@ -3414,13 +3414,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "默认布局无法被覆盖保存" + "value" : "不允許儲存覆寫「預設」版面配置" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "默认布局无法被覆盖保存" + "value" : "不允許儲存覆寫「預設」版面配置" } } } @@ -3443,13 +3443,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "needs_review", - "value" : "提示: 手动锁定屏幕方向避免串流显示异常" + "value" : "提示:手動鎖定螢幕方向以避免顯示異常" } }, "zh-HK" : { "stringUnit" : { "state" : "needs_review", - "value" : "提示: 手动锁定屏幕方向避免串流显示异常" + "value" : "提示:手動鎖定螢幕方向以避免顯示異常" } } } @@ -3472,13 +3472,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "正在搜索网络中的主机 ..." + "value" : "正在網路上搜尋電腦..." } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "正在搜索网络中的主机 ..." + "value" : "正在網路上搜尋電腦..." } } } @@ -3495,13 +3495,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "选择主机" + "value" : "選取主機" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "选择主机" + "value" : "選取主機" } } } @@ -3518,13 +3518,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "选择新主机" + "value" : "選取新主機" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "选择新主机" + "value" : "選取新主機" } } } @@ -3541,13 +3541,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "灵敏度X: %.2f" + "value" : "靈敏度X:%.2f" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "灵敏度X: %.2f" + "value" : "靈敏度X:%.2f" } } } @@ -3564,13 +3564,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "灵敏度Y: %.2f" + "value" : "靈敏度Y:%.2f" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "灵敏度Y: %.2f" + "value" : "靈敏度Y:%.2f" } } } @@ -3587,13 +3587,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "串流分辨率:" + "value" : "設定電腦/遊戲解析度:" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "串流分辨率:" + "value" : "設定電腦/遊戲解析度:" } } } @@ -3610,13 +3610,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "设置" + "value" : "設定" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "设置" + "value" : "設定" } } } @@ -3633,13 +3633,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "按键形状 (r - 圆形, s - 方形)" + "value" : "形狀 (r - 圓形, s - 方形)" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "按键形状 (r - 圆形, s - 方形)" + "value" : "形狀 (r - 圓形, s - 方形)" } } } @@ -3656,13 +3656,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "显示应用" + "value" : "顯示應用程式" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "显示应用" + "value" : "顯示應用程式" } } } @@ -3679,13 +3679,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "控件大小" + "value" : "大小" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "控件大小" + "value" : "大小" } } } @@ -3702,13 +3702,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "控件大小: %.2f" + "value" : "大小:%.2f" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "控件大小: %.2f" + "value" : "大小:%.2f" } } } @@ -3725,13 +3725,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "串流中打开菜单滑动距离: %.2f x 屏幕宽度" + "value" : "串流中選單的滑動距離:%.2f * 螢幕寬度" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "串流中打开菜单滑动距离: %.2f x 屏幕宽度" + "value" : "串流中選單的滑動距離:%.2f * 螢幕寬度" } } } @@ -3748,13 +3748,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "网络带宽不足, 请降低串流码率" + "value" : "與電腦的連線緩慢,請降低您的位元率" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "网络带宽不足, 请降低串流码率" + "value" : "與電腦的連線緩慢,請降低您的位元率" } } } @@ -3771,13 +3771,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "双声道 (iOS18+支持环绕声)" + "value" : "立體聲 (iOS18+ 提供環繞音效)" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "双声道 (iOS18+支持环绕声)" + "value" : "立體聲 (iOS18+ 提供環繞音效)" } } } @@ -3794,13 +3794,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "成功发送唤醒请求. 主机唤醒需要一定时间. 如果主机未被唤醒, 请检查Wake-on-LAN设置" + "value" : "已成功傳送喚醒請求。電腦可能需要一些時間才能喚醒。如果它一直沒有喚醒,請確保已正確設定網路喚醒。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "成功发送唤醒请求. 主机唤醒需要一定时间. 如果主机未被唤醒, 请检查Wake-on-LAN设置" + "value" : "已成功傳送喚醒請求。電腦可能需要一些時間才能喚醒。如果它一直沒有喚醒,請確保已正確設定網路喚醒。" } } } @@ -3817,13 +3817,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "在串流视图中轻按%d根手指修改虚拟手柄布局" + "value" : "在串流畫面中輕點%d指以變更螢幕控制器版面配置" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "在串流视图中轻按%d根手指修改虚拟手柄布局" + "value" : "在串流畫面中輕點%d指以變更螢幕控制器版面配置" } } } @@ -3840,13 +3840,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "自定义屏显手柄触控无响应区面积: %d%%" + "value" : "自訂螢幕控制器點選手指排除區域大小:%d%%" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "自定义屏显手柄触控无响应区面积: %d%%" + "value" : "自訂螢幕控制器點選手指排除區域大小:%d%%" } } } @@ -3863,13 +3863,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "点击需要输入文字的位置, 自动抬升视图 " + "value" : "點選您要輸入文字的地方,畫面將自動升起 " } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "点击需要输入文字的位置, 自动抬升视图 " + "value" : "點選您要輸入文字的地方,畫面將自動升起 " } } } @@ -3886,13 +3886,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "测试网络" + "value" : "測試網路" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "测试网络" + "value" : "測試網路" } } } @@ -3909,13 +3909,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "连接终止, 错误码: %@" + "value" : "連線已終止,錯誤碼:%@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "连接终止, 错误码: %@" + "value" : "連線已終止,錯誤碼:%@" } } } @@ -3932,13 +3932,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "已经没有修改可以撤消" + "value" : "沒有可復原的變更" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "已经没有修改可以撤消" + "value" : "沒有可復原的變更" } } } @@ -3955,13 +3955,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "提示: 从屏幕连续滑动一定距离退出远程桌面. 滑动边缘和距离取决于菜单设置." + "value" : "提示:從螢幕邊緣滑動一段距離(由滑動和離開設定配置)以中斷與您電腦的連線" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "\\n提示: 从屏幕连续滑动一定距离退出远程桌面. 滑动边缘和距离取决于菜单设置." + "value" : "提示:從螢幕邊緣滑動一段距離(由滑動和離開設定配置)以中斷與您電腦的連線" } } } @@ -4007,13 +4007,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "轻按%d根手指, 切换本地软键盘" + "value" : "若要切換本機鍵盤:輕點%d指" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "轻按%d根手指, 切换本地软键盘" + "value" : "若要切換本機鍵盤:輕點%d指" } } } @@ -4053,13 +4053,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "触屏与控制" + "value" : "觸控與控制" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "触屏与控制" + "value" : "觸控與控制" } } } @@ -4076,13 +4076,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "触控模式 - 双击屏幕拖动, 支持屏显手柄" + "value" : "觸控板模式 - 輕點兩下來拖曳,可使用螢幕控制器" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "触控模式 - 双击屏幕拖动, 支持屏显手柄" + "value" : "觸控板模式 - 輕點兩下來拖曳,可使用螢幕控制器" } } } @@ -4099,13 +4099,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "触控模式 - 适用MacOS单点触控" + "value" : "觸控模式 - 適用於 macOS 直接觸控" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "触控模式 - 适用MacOS单点触控" + "value" : "觸控模式 - 適用於 macOS 直接觸控" } } } @@ -4122,13 +4122,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "触控模式 - 不支持屏显手柄,鼠标" + "value" : "觸控模式 - 無螢幕控制器與滑鼠支援" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "触控模式 - 不支持屏显手柄,鼠标" + "value" : "觸控模式 - 無螢幕控制器與滑鼠支援" } } } @@ -4145,13 +4145,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "触控模式 - 支持屏显手柄,鼠标" + "value" : "觸控模式 - 具備螢幕控制器與滑鼠支援" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "触控模式 - 支持屏显手柄,鼠标" + "value" : "觸控模式 - 具備螢幕控制器與滑鼠支援" } } } @@ -4168,13 +4168,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "触点灵敏度: %d%%" + "value" : "觸控指標速度:%d%%" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "触点灵敏度: %d%%" + "value" : "觸控指標速度:%d%%" } } } @@ -4191,13 +4191,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "调节屏幕靠右 %d%%区域的触点灵敏度" + "value" : "觸控指標速度:在右側螢幕的%d%%上縮放" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "调节屏幕靠右 %d%%区域的触点灵敏度" + "value" : "觸控指標速度:在右側螢幕的%d%%上縮放" } } } @@ -4220,13 +4220,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "- 触控板: 拖移方式与Windows笔记本电脑触控板相同, 快速双击的第2下开始进入拖动状态.\n\n- 多点触控: 不支持macOS.\n\n- 单点鼠标: 可为macOS提供类触屏体验." + "value" : "- 觸控板:拖曳方式與 Windows 筆記型電腦觸控板完全相同。快速輕點兩下,從第二次輕點開始進入拖曳模式。\n\n- 原生觸控:即多點觸控傳遞,macOS 不支援。\n\n- 絕對觸控:在 macOS 上提供類似觸控螢幕的體驗。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "- 触控板: 拖移方式与Windows笔记本电脑触控板相同, 快速双击的第2下开始进入拖动状态.\n\n- 多点触控: 不支持macOS.\n\n- 单点鼠标: 可为macOS提供类触屏体验." + "value" : "- 觸控板:拖曳方式與 Windows 筆記型電腦觸控板完全相同。快速輕點兩下,從第二次輕點開始進入拖曳模式。\n\n- 原生觸控:即多點觸控傳遞,macOS 不支援。\n\n- 絕對觸控:在 macOS 上提供類似觸控螢幕的體驗。" } } } @@ -4243,13 +4243,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "该设备不支持" + "value" : "此裝置不支援" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "该设备不支持" + "value" : "此裝置不支援" } } } @@ -4266,13 +4266,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "视频" + "value" : "視訊" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "视频" + "value" : "視訊" } } } @@ -4289,13 +4289,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "视频高度" + "value" : "視訊高度" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "视频高度" + "value" : "視訊高度" } } } @@ -4312,13 +4312,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "视频流: %dx%d %.2f FPS (编解码: %@)\n网络丢帧: %.2f%%\n平均网络延迟: %@%@" + "value" : "視訊串流:%dx%d %.2f FPS (編解碼器:%@)\n網路掉幀:%.2f%%\n平均網路延遲:%@%@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "视频流: %dx%d %.2f FPS (编解码: %@)\n网络丢帧: %.2f%%\n平均网络延迟: %@%@" + "value" : "視訊串流:%dx%d %.2f FPS (編解碼器:%@)\n網路掉幀:%.2f%%\n平均網路延遲:%@%@" } } } @@ -4335,13 +4335,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "视频宽度" + "value" : "視訊寬度" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "视频宽度" + "value" : "視訊寬度" } } } @@ -4358,13 +4358,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "查看所有应用" + "value" : "檢視所有應用程式" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "查看所有应用" + "value" : "檢視所有應用程式" } } } @@ -4381,13 +4381,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "唤醒PC" + "value" : "喚醒電腦" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "唤醒PC" + "value" : "喚醒電腦" } } } @@ -4410,13 +4410,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "needs_review", - "value" : "开发者: True砖家 (B站)" + "value" : "聯絡開發者 True砖家" } }, "zh-HK" : { "stringUnit" : { "state" : "needs_review", - "value" : "开发者: True砖家 (B站)" + "value" : "聯絡開發者 True砖家" } } } @@ -4433,13 +4433,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "网络状态较差. 请降低码率或更换可靠的网络连接." + "value" : "您的網路連線效能不佳。請降低您的視訊位元率設定或嘗試更快的連線。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "网络状态较差. 请降低码率或更换可靠的网络连接." + "value" : "您的網路連線效能不佳。請降低您的視訊位元率設定或嘗試更快的連線。" } } } @@ -4462,17 +4462,18 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "- 开启YUV 4:4:4 选项可提供更高色彩保真度, 但对带宽要求也更高. \n\n- 由于Apple并未提供API判断设备是否支持YUV 4:4:4视频硬解码, 您需要自行打开该选项确认是否可用. 某些设备会因此产生视频亮度丢失的问题." + "value" : "- 啟用 YUV 4:4:4 選項可提供更高的色彩保真度,但也需要更大的頻寬。\n\n- 由於 Apple 未提供 API 來判斷裝置是否支援 YUV 4:4:4 視訊的硬體解碼,您需要手動啟用此選項以驗證相容性。在某些裝置上,這可能會導致視訊亮度遺失等問題。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "- 开启YUV 4:4:4 选项可提供更高色彩保真度, 但对带宽要求也更高. \n\n- 由于Apple并未提供API判断设备是否支持YUV 4:4:4视频硬解码, 您需要自行打开该选项确认是否可用. 某些设备会因此产生视频亮度丢失的问题." + "value" : "- 啟用 YUV 4:4:4 選項可提供更高的色彩保真度,但也需要更大的頻寬。\n\n- 由於 Apple 未提供 API 來判斷裝置是否支援 YUV 4:4:4 視訊的硬體解碼,您需要手動啟用此選項以驗證相容性。在某些裝置上,這可能會導致視訊亮度遺失等問題。" } } } } }, "version" : "1.0" -} \ No newline at end of file +} + From 00491d3bcef87cb8e3b81df5ced0150cd1fe2329 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Thu, 17 Jul 2025 22:08:13 +0800 Subject: [PATCH 04/14] tweak hostSpinner --- VoidLink/HostCardView.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/VoidLink/HostCardView.m b/VoidLink/HostCardView.m index 497b85ea..cfe45f99 100644 --- a/VoidLink/HostCardView.m +++ b/VoidLink/HostCardView.m @@ -50,13 +50,16 @@ static const float REFRESH_CYCLE = 2.0f; +- (BOOL)isIPhone{ + return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone); +} - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { buttonLabelFontSize = 15*_sizeFactor; longPressFired = false; - computerIconMonitorCenterYOffset = -4.3523*_sizeFactor; + computerIconMonitorCenterYOffset = [self isIPhone] ? -3.62*_sizeFactor : -4.175*_sizeFactor; iconAndButtonSpacing = 37*_sizeFactor; buttonHeight = 39*_sizeFactor; defaultBlue = [ThemeManager appPrimaryColor]; @@ -94,6 +97,8 @@ static const float REFRESH_CYCLE = 2.0f; } + + - (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { From ba453f940561f1e1fd8675c6688d44551a6a7aed Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Thu, 17 Jul 2025 22:46:56 +0800 Subject: [PATCH 05/14] aboutView draft --- VoidLink.xcodeproj/project.pbxproj | 8 ++ VoidLink/.DS_Store | Bin 10244 -> 10244 bytes VoidLink/AboutView.swift | 86 ++++++++++++++++++ VoidLink/AboutViewController.swift | 37 ++++++++ .../AppIconMedium.imageset/AppIconMedium.png | Bin 0 -> 39669 bytes .../AppIconMedium.imageset/Contents.json | 21 +++++ VoidLink/Localization/Localizable.xcstrings | 49 ++++++++++ .../ViewControllers/MainFrameViewController.m | 12 ++- 8 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 VoidLink/AboutView.swift create mode 100644 VoidLink/AboutViewController.swift create mode 100644 VoidLink/Images.xcassets/AppIconMedium.imageset/AppIconMedium.png create mode 100644 VoidLink/Images.xcassets/AppIconMedium.imageset/Contents.json diff --git a/VoidLink.xcodeproj/project.pbxproj b/VoidLink.xcodeproj/project.pbxproj index da2f9d46..f7e9d4fc 100644 --- a/VoidLink.xcodeproj/project.pbxproj +++ b/VoidLink.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 1829CE3F2E2154DF0074A99A /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1829CE3E2E2154DF0074A99A /* SceneDelegate.m */; }; 18341A042DEB662C0094E5F2 /* FixedTintImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 18341A032DEB662C0094E5F2 /* FixedTintImageView.m */; }; 187F4B282E04658A00EECC59 /* WidgetPanelStackView.m in Sources */ = {isa = PBXBuildFile; fileRef = 187F4B272E04658A00EECC59 /* WidgetPanelStackView.m */; }; + 1888C1922E24C33100108730 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1888C1912E24C33100108730 /* AboutViewController.swift */; }; + 1888C1932E24C41000108730 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1888C1902E24C2C800108730 /* AboutView.swift */; }; 189AD29A2DE7446000E24B88 /* HostCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 189AD2992DE7446000E24B88 /* HostCollectionViewController.m */; }; 840751B32BF49C050096B657 /* CustomTapGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 840751B22BF49C050096B657 /* CustomTapGestureRecognizer.m */; }; 841BAC922DE35D6B00DA639E /* ThemeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 841BAC912DE35D6B00DA639E /* ThemeManager.m */; }; @@ -137,6 +139,8 @@ 18341A032DEB662C0094E5F2 /* FixedTintImageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FixedTintImageView.m; sourceTree = ""; }; 187F4B232E04633100EECC59 /* WidgetPanelStackView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WidgetPanelStackView.h; sourceTree = ""; }; 187F4B272E04658A00EECC59 /* WidgetPanelStackView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WidgetPanelStackView.m; sourceTree = ""; }; + 1888C1902E24C2C800108730 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; + 1888C1912E24C33100108730 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; 189AD2962DE743B700E24B88 /* HostCollectionViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HostCollectionViewController.h; sourceTree = ""; }; 189AD2992DE7446000E24B88 /* HostCollectionViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HostCollectionViewController.m; sourceTree = ""; }; 18BF141D2E0D7A00009DEE4F /* VoidLink v1.0.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "VoidLink v1.0.xcdatamodel"; sourceTree = ""; }; @@ -513,6 +517,8 @@ FBDE86DF19F7A837001C18A8 /* UIComputerView.m */, FBDE86E419F82297001C18A8 /* UIAppView.h */, FBDE86E519F82297001C18A8 /* UIAppView.m */, + 1888C1902E24C2C800108730 /* AboutView.swift */, + 1888C1912E24C33100108730 /* AboutViewController.swift */, FB89463719F6473800339C8A /* Launch Screen.xib */, FB290D0819B2C406004C83CF /* Images.xcassets */, FB290D0519B2C406004C83CF /* Limelight.xcdatamodeld */, @@ -917,6 +923,7 @@ DC1F5A07206436B20037755F /* ConnectionHelper.m in Sources */, FB89462E19F646E200339C8A /* PairManager.m in Sources */, FB9AFD371A7E02DB00872C98 /* HttpRequest.m in Sources */, + 1888C1932E24C41000108730 /* AboutView.swift in Sources */, FB4678ED1A50C40900377732 /* OnScreenControls.m in Sources */, 84E1CDFA2C4FE83400547904 /* SwiftLocalizationHelper.swift in Sources */, FB290D0019B2C406004C83CF /* main.m in Sources */, @@ -926,6 +933,7 @@ 84351B0E2C29ACCB00A77989 /* LayoutOnScreenControls.m in Sources */, FB89462A19F646E200339C8A /* ControllerSupport.m in Sources */, FB9AFD3D1A7E111600872C98 /* AppAssetResponse.m in Sources */, + 1888C1922E24C33100108730 /* AboutViewController.swift in Sources */, FBD349621A0089F6002D2A60 /* DataManager.m in Sources */, FB4A23B81A9D3637004D2EF2 /* LoadingFrameViewController.m in Sources */, FB9AFD3A1A7E05CE00872C98 /* ServerInfoResponse.m in Sources */, diff --git a/VoidLink/.DS_Store b/VoidLink/.DS_Store index e3c74898749c5b3d69ebf15c50ba65a6f44f1075..209607ba900052e630de55f6caf3c2d974fbd758 100644 GIT binary patch delta 888 zcmZn(XbG6$FKWubz`)4BAi%&-!Vt!g$&kuW&Y-uka2or>2Hwr=94s8EAQk)!jtof* z`3$AVsu+qH${8{l(iloSbMljua`KZHzy>k=VqjpB{SO8VARviFw=+XNLmop3LlHwh zLk>gEWCd|KHpVEB#>rj+hLf`e`8T@=2r|oxFt{;fGE^|6GNdqsFl2)DcrxTNBr>Ek zq)tu{l+t6|1=0n!?LWvi2K-tI1f?2gDE>T@=Xkn?NU}9)k ztD{hDX=I?IU}9`mTg%BIs;qAv6rY`wo0s1;SxZ8mv3qiruo6#Ma8X`PeqK5#PB!ll zp3KB8$l%M6%8*lb1>GZ?2aJVG@*L2xG`+$Ye-i@Bycu?8$LrMm!9laD?icSa^3cyTUIPP5}l6 Luz9qM(glnF%7^Fv delta 166 zcmZn(XbG6$FRI4Cz`)4BAi%(o&yd5A!;s35%uuqia2or>2Hwr=94s7+YMbW>crs5e z715Y1Dr&bmU2FoAurz}&Lq0=3Lmop8LncEyLk2_1Yn{7#R5`7f9}$%q`8k k`Hd7W?kO( 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 literal 0 HcmV?d00001 diff --git a/VoidLink/Images.xcassets/AppIconMedium.imageset/Contents.json b/VoidLink/Images.xcassets/AppIconMedium.imageset/Contents.json new file mode 100644 index 00000000..53cad8b1 --- /dev/null +++ b/VoidLink/Images.xcassets/AppIconMedium.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "AppIconMedium.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index ab4b6d92..8a48fd2d 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -1802,6 +1802,29 @@ } } }, + "From the player community, to the player community." : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "从玩家社区中来,到玩家社区中去。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "從玩家社群中來,到玩家社群中去。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "從玩家社群中來,到玩家社群中去。" + } + } + } + }, "Gestures" : { "extractionState" : "manual", "localizations" : { @@ -2078,6 +2101,29 @@ } } }, + "Join us" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "加入我们" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "加入我們" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "加入我們" + } + } + } + }, "Launch" : { "extractionState" : "manual", "localizations" : { @@ -4253,6 +4299,9 @@ } } } + }, + "Version %@" : { + }, "Video" : { "extractionState" : "manual", diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 767958cd..2afae369 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -1342,13 +1342,23 @@ static NSMutableSet* hostList; button.frame = CGRectMake(0, 0, buttonHeight, buttonHeight); // 添加点击事件 - // [button addTarget:self action:@selector(addDeviceTapped) forControlEvents:UIControlEventTouchUpInside]; + [button addTarget:self action:@selector(helpButtonTapped) forControlEvents:UIControlEventTouchUpInside]; // 创建 UIBarButtonItem UIBarButtonItem *barItem = [[UIBarButtonItem alloc] initWithCustomView:button]; return barItem; } +- (void)helpButtonTapped{ + if (@available(iOS 13.0, *)) { + AboutViewController *aboutVC = [[AboutViewController alloc] init]; + aboutVC.modalPresentationStyle = UIModalPresentationFormSheet; + [self presentViewController:aboutVC animated:YES completion:nil]; + } else { + // Fallback on earlier versions + } +} + - (CGFloat)getStandardNavBarHeight{ return [self isIPhone] ? UINavigationBarHeightIPhone : UINavigationBarHeightIPad; } From 12ad168e13267dc13556b3bb37979ece9368ea55 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Thu, 17 Jul 2025 23:41:11 +0800 Subject: [PATCH 06/14] about page content udpate --- VoidLink/AboutView.swift | 24 ++++++++++++++++----- VoidLink/AboutViewController.swift | 4 ++++ VoidLink/Localization/Localizable.xcstrings | 6 ++++-- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/VoidLink/AboutView.swift b/VoidLink/AboutView.swift index eed30c73..206967aa 100644 --- a/VoidLink/AboutView.swift +++ b/VoidLink/AboutView.swift @@ -14,11 +14,11 @@ public struct AboutView: View { @Environment(\.presentationMode) var presentationMode public var body: some View { - VStack(spacing: 20) { + VStack(spacing: 10) { // App 图标 Image(uiImage: UIImage(named: "AppIconMedium") ?? UIImage()) .resizable() - .frame(width: 100, height: 100) + .frame(width: 80, height: 80) .clipShape(RoundedRectangle(cornerRadius: 36)) // App 名称 @@ -35,11 +35,25 @@ public struct AboutView: View { // 说明文字 Text(SwiftLocalizationHelper.localizedString(forKey: "From the player community, to the player community.")) - .multilineTextAlignment(.leading) + .multilineTextAlignment(.center) .lineLimit(nil) .frame(maxWidth: 570) // ✅ 避免 Text 被拉得太宽无法换行 - .padding() - + + if Locale.preferredLanguages.first!.hasPrefix("zh-Hans") { + if #available(iOS 14.0, *) { + Text("闽ICP备17012590号-3A\n主办单位:福州创图信息技术有限公司").font(.caption2) + .multilineTextAlignment(.center) + .lineLimit(nil) + .frame(maxWidth: 570) // ✅ 避免 Text 被拉得太宽无法换行 + .padding() + } else { + Text("闽ICP备17012590号-3A\n主办单位:福州创图信息技术有限公司").font(.caption) + .multilineTextAlignment(.center) + .lineLimit(nil) + .frame(maxWidth: 570) // ✅ 避免 Text 被拉得太宽无法换行 + .padding() + } + } // 链接按钮 if #available(iOS 14.0, *) { Link(SwiftLocalizationHelper.localizedString(forKey: "Join us"), destination: URL(string: "https://example.com")!) diff --git a/VoidLink/AboutViewController.swift b/VoidLink/AboutViewController.swift index 9b996ae5..b82eb1f8 100644 --- a/VoidLink/AboutViewController.swift +++ b/VoidLink/AboutViewController.swift @@ -29,9 +29,13 @@ import SwiftUI // 🎯 设置弹窗大小 preferredContentSize = CGSize(width: 600, height: 450) + //preferredContentSize = CGSize(width:150, height:120 ) // ⚠️ 一定要设置以下样式才会生效 preferredContentSize modalPresentationStyle = .formSheet } } +func isIPhone() -> Bool { + return UIDevice.current.userInterfaceIdiom == .phone +} diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index 4a40457e..07db9814 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -4521,8 +4521,10 @@ } } } + }, + "闽ICP备17012590号-3A\n主办单位:福州创图信息技术有限公司" : { + } }, "version" : "1.0" -} - +} \ No newline at end of file From 334788c26f21e741b09f301d7422fdf4cb628741 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sat, 19 Jul 2025 14:47:04 +0800 Subject: [PATCH 07/14] - fix appview background update issue - relocate chevron view for ipad osw tool - improve edge slide detection for native touchHandler --- VoidLink/Base.lproj/iPad.storyboard | 12 ++++---- VoidLink/Input/NativeTouchHandler.m | 29 ++++++++---------- VoidLink/Input/PureNativeTouchHandler.m | 30 +++++++++---------- VoidLink/Input/RelativeTouchHandler.h | 1 - VoidLink/Input/RelativeTouchHandler.m | 3 -- VoidLink/UIAppView.h | 8 +++++ VoidLink/UIAppView.m | 27 ++++++++--------- .../ViewControllers/MainFrameViewController.h | 2 +- .../ViewControllers/MainFrameViewController.m | 9 ++++++ 9 files changed, 64 insertions(+), 57 deletions(-) diff --git a/VoidLink/Base.lproj/iPad.storyboard b/VoidLink/Base.lproj/iPad.storyboard index 7ec05301..aadae408 100644 --- a/VoidLink/Base.lproj/iPad.storyboard +++ b/VoidLink/Base.lproj/iPad.storyboard @@ -1262,7 +1262,7 @@ - + @@ -1304,7 +1304,7 @@ - + @@ -1659,19 +1659,19 @@ - + - + - + - + diff --git a/VoidLink/Input/NativeTouchHandler.m b/VoidLink/Input/NativeTouchHandler.m index 87833bfb..0c82e10b 100644 --- a/VoidLink/Input/NativeTouchHandler.m +++ b/VoidLink/Input/NativeTouchHandler.m @@ -19,7 +19,6 @@ StreamView* streamView; TemporarySettings* currentSettings; bool activateCoordSelector; - bool touchPointSpawnedAtUpperScreenEdge; CGFloat pointerVelocityDividerLocationByPoints; uint16_t touchMoveEventIntervalUs; @@ -35,7 +34,8 @@ NSMutableSet *activePointerIds; //pointerId Set for active touches. NSMutableSet *pointerIdPool; //pre-defined pool of pointerIds. NSMutableSet *unassignedPointerIds; - + NSMutableSet *blacklistedTouches; + NSMutableDictionary *pointerObjDict; CGFloat slideGestureVerticalThreshold; @@ -59,9 +59,8 @@ [self->pointerIdPool addObject:@(i)]; } self->activePointerIds = [NSMutableSet set]; - // self->excludedPointerIds = [[NSMutableSet alloc] init]; - self->touchPointSpawnedAtUpperScreenEdge = false; - + self->blacklistedTouches = [NSMutableSet set]; + self->asyncNativeTouch = settings.asyncNativeTouchPriority.intValue != AsyncNativeTouchOff; switch(settings.asyncNativeTouchPriority.intValue){ @@ -156,28 +155,27 @@ // generate & populate pointerId into NSDict & NSSet, called in touchesBegan - (void)handleTouchDown:(UITouch*)touch{ //populate pointerId - uintptr_t memAddrValue = (uintptr_t)touch; + NSNumber* touchAddrObj = @((uintptr_t)touch); unassignedPointerIds = [pointerIdPool mutableCopy]; //reset unassignedPointerIds [unassignedPointerIds minusSet:activePointerIds]; uint8_t pointerId = [[unassignedPointerIds anyObject] unsignedIntValue]; - [pointerIdDict setObject:@(pointerId) forKey:@(memAddrValue)]; + [pointerIdDict setObject:@(pointerId) forKey:touchAddrObj]; [activePointerIds addObject:@(pointerId)]; //check if touch point is spawned on the left or right upper half screen edges, event to remote PC. this is for better handling in-stream slide gesture CGPoint initialPoint = [touch locationInView:self->streamView]; if(initialPoint.y < slideGestureVerticalThreshold && (initialPoint.x < EDGE_TOLERANCE || initialPoint.x > screenWidthWithThreshold)) { - self->touchPointSpawnedAtUpperScreenEdge = true; - // [excludedPointerIds addObject:@(pointerId)]; + [blacklistedTouches addObject:touchAddrObj]; } } // remove pointerId in touchesEnded or touchesCancelled - (void)removePointerId:(UITouch*)touch{ - uintptr_t memAddrValue = (uintptr_t)touch; - NSNumber* pointerIdObj = [pointerIdDict objectForKey:@(memAddrValue)]; + NSNumber* touchAddrObj = @((uintptr_t)touch); + NSNumber* pointerIdObj = [pointerIdDict objectForKey:touchAddrObj]; if(pointerIdObj != nil){ [activePointerIds removeObject:pointerIdObj]; - [pointerIdDict removeObjectForKey:@(memAddrValue)]; + [pointerIdDict removeObjectForKey:touchAddrObj]; // if([excludedPointerIds containsObject:pointerIdObj]) [excludedPointerIds removeObject:pointerIdObj]; // remove pointer id from excludedPointerId NSSet } } @@ -190,7 +188,8 @@ - (void)sendTouchEvent:(UITouch*)touch withTouchtype:(uint8_t)touchType{ - if(touchPointSpawnedAtUpperScreenEdge && touchType != LI_TOUCH_EVENT_UP) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash + //if(touchPointSpawnedAtUpperScreenEdge && touchType != LI_TOUCH_EVENT_UP) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash + if([blacklistedTouches containsObject:@((uintptr_t)touch)]) return; CGPoint targetCoords; //NSLog(@"selecting coords: %d", touch.phase == UITouchPhaseMoved); @@ -263,9 +262,9 @@ [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_UP]; //send touch event before remove pointerId [self removePointerId:touch]; //then remove pointerId if(self->activateCoordSelector) [self removePointerObjFromDict:touch]; + [self->blacklistedTouches removeObject:@((uintptr_t)touch)]; } //if(self->touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count]) - self->touchPointSpawnedAtUpperScreenEdge = false; }); else{ for (UITouch* touch in touches){ @@ -275,7 +274,6 @@ [self removePointerId:touch]; //then remove pointerId if(self->activateCoordSelector) [self removePointerObjFromDict:touch]; } - if(self->touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count]) self->touchPointSpawnedAtUpperScreenEdge = false; } } @@ -309,7 +307,6 @@ - (CGPoint)selectCoordsFor:(UITouch *)touch{ NativeTouchPointer *pointer = [pointerObjDict objectForKey:@((uintptr_t)touch)]; if(pointer == nil) return CGPointMake(0, 0); - // NSLog(@"using relative Coords: intialX %f, divider loc %f", pointer.initialPoint.x, pointerVelocityDividerLocationByPoints); return pointer.useRelativeCoords ? pointer.latestRelativePoint : pointer.latestPoint; } diff --git a/VoidLink/Input/PureNativeTouchHandler.m b/VoidLink/Input/PureNativeTouchHandler.m index cc34d5eb..3b029709 100644 --- a/VoidLink/Input/PureNativeTouchHandler.m +++ b/VoidLink/Input/PureNativeTouchHandler.m @@ -18,7 +18,6 @@ StreamView* streamView; TemporarySettings* currentSettings; bool activateCoordSelector; - bool touchPointSpawnedAtUpperScreenEdge; CGFloat pointerVelocityDividerLocationByPoints; uint16_t touchMoveEventIntervalUs; @@ -34,7 +33,8 @@ NSMutableSet *activePointerIds; //pointerId Set for active touches. NSMutableSet *pointerIdPool; //pre-defined pool of pointerIds. NSMutableSet *unassignedPointerIds; - + NSMutableSet *blacklistedTouches; + NSMutableDictionary *pointerObjDict; CGFloat slideGestureVerticalThreshold; @@ -58,9 +58,8 @@ [self->pointerIdPool addObject:@(i)]; } self->activePointerIds = [NSMutableSet set]; - // self->excludedPointerIds = [[NSMutableSet alloc] init]; - self->touchPointSpawnedAtUpperScreenEdge = false; - + self->blacklistedTouches = [NSMutableSet set]; + self->asyncNativeTouch = settings.asyncNativeTouchPriority.intValue != AsyncNativeTouchOff; switch(settings.asyncNativeTouchPriority.intValue){ @@ -155,28 +154,27 @@ // generate & populate pointerId into NSDict & NSSet, called in touchesBegan - (void)handleTouchDown:(UITouch*)touch{ //populate pointerId - uintptr_t memAddrValue = (uintptr_t)touch; + NSNumber* touchAddrObj = @((uintptr_t)touch); unassignedPointerIds = [pointerIdPool mutableCopy]; //reset unassignedPointerIds [unassignedPointerIds minusSet:activePointerIds]; uint8_t pointerId = [[unassignedPointerIds anyObject] unsignedIntValue]; - [pointerIdDict setObject:@(pointerId) forKey:@(memAddrValue)]; + [pointerIdDict setObject:@(pointerId) forKey:touchAddrObj]; [activePointerIds addObject:@(pointerId)]; //check if touch point is spawned on the left or right upper half screen edges, event to remote PC. this is for better handling in-stream slide gesture CGPoint initialPoint = [touch locationInView:self->streamView]; if(initialPoint.y < slideGestureVerticalThreshold && (initialPoint.x < EDGE_TOLERANCE || initialPoint.x > screenWidthWithThreshold)) { - self->touchPointSpawnedAtUpperScreenEdge = true; - // [excludedPointerIds addObject:@(pointerId)]; + [blacklistedTouches addObject:touchAddrObj]; } } // remove pointerId in touchesEnded or touchesCancelled - (void)removePointerId:(UITouch*)touch{ - uintptr_t memAddrValue = (uintptr_t)touch; - NSNumber* pointerIdObj = [pointerIdDict objectForKey:@(memAddrValue)]; + NSNumber* touchAddrObj = @((uintptr_t)touch); + NSNumber* pointerIdObj = [pointerIdDict objectForKey:touchAddrObj]; if(pointerIdObj != nil){ [activePointerIds removeObject:pointerIdObj]; - [pointerIdDict removeObjectForKey:@(memAddrValue)]; + [pointerIdDict removeObjectForKey:touchAddrObj]; // if([excludedPointerIds containsObject:pointerIdObj]) [excludedPointerIds removeObject:pointerIdObj]; // remove pointer id from excludedPointerId NSSet } } @@ -189,8 +187,9 @@ - (void)sendTouchEvent:(UITouch*)touch withTouchtype:(uint8_t)touchType{ - if(touchPointSpawnedAtUpperScreenEdge && touchType != LI_TOUCH_EVENT_UP) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash - + //if(touchPointSpawnedAtUpperScreenEdge && touchType != LI_TOUCH_EVENT_UP) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash + if([blacklistedTouches containsObject:@((uintptr_t)touch)]) return; + CGPoint targetCoords; //NSLog(@"selecting coords: %d", touch.phase == UITouchPhaseMoved); // NSLog(@"excluded count: %d", (uint32_t)[excludedPointerIds count]); @@ -252,8 +251,8 @@ [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_UP]; //send touch event before remove pointerId [self removePointerId:touch]; //then remove pointerId if(self->activateCoordSelector) [self removePointerObjFromDict:touch]; + [self->blacklistedTouches removeObject:@((uintptr_t)touch)]; } - self->touchPointSpawnedAtUpperScreenEdge = false; }); else{ for (UITouch* touch in touches){ @@ -261,7 +260,6 @@ [self removePointerId:touch]; //then remove pointerId if(self->activateCoordSelector) [self removePointerObjFromDict:touch]; } - if(self->touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count]) self->touchPointSpawnedAtUpperScreenEdge = false; } } diff --git a/VoidLink/Input/RelativeTouchHandler.h b/VoidLink/Input/RelativeTouchHandler.h index d856d468..0bd4a512 100644 --- a/VoidLink/Input/RelativeTouchHandler.h +++ b/VoidLink/Input/RelativeTouchHandler.h @@ -19,7 +19,6 @@ NS_ASSUME_NONNULL_BEGIN @interface RelativeTouchHandler : UIResponder @property (nonatomic, readonly) CustomTapGestureRecognizer* mouseRightClickTapRecognizer; // this object will be passed to onscreencontrol class for areVirtualControllerTaps flag setting -@property (nonatomic, strong) CADisplayLink *displayLink; - (id)initWithView:(StreamView*)view andSettings:(TemporarySettings*)settings; diff --git a/VoidLink/Input/RelativeTouchHandler.m b/VoidLink/Input/RelativeTouchHandler.m index 0f67455d..46a2d932 100644 --- a/VoidLink/Input/RelativeTouchHandler.m +++ b/VoidLink/Input/RelativeTouchHandler.m @@ -12,9 +12,6 @@ #include - -static const int REFERENCE_WIDTH = 1280; -static const int REFERENCE_HEIGHT = 720; static const float QUICK_TAP_TIME_INTERVAL = 0.2; @implementation RelativeTouchHandler { diff --git a/VoidLink/UIAppView.h b/VoidLink/UIAppView.h index 9f2504cb..9554d036 100644 --- a/VoidLink/UIAppView.h +++ b/VoidLink/UIAppView.h @@ -9,6 +9,11 @@ #import #import "TemporaryApp.h" +@protocol AppViewUpdateLoopDelegate +- (bool)isStreaming; +@end + + @protocol AppCallback - (void) appClicked:(TemporaryApp*)app view:(UIView*)view; @@ -22,6 +27,9 @@ @interface UIAppView : UIButton #endif + +@property (nonatomic, weak) id updateLoopDelegate; // Delegate property + - (id) initWithApp:(TemporaryApp*)app cache:(NSCache*)cache andCallback:(id)callback; - (void) updateAppImage; diff --git a/VoidLink/UIAppView.m b/VoidLink/UIAppView.m index 9d98d353..71cfbfda 100644 --- a/VoidLink/UIAppView.m +++ b/VoidLink/UIAppView.m @@ -48,6 +48,7 @@ static UIImage* noImage; _appImage = [[UIImageView alloc] initWithFrame:self.frame]; [_appImage setImage:noImage]; + [self addSubview:_appImage]; // Use UIContextMenuInteraction on iOS 13.0+ and a standard UILongPressGestureRecognizer @@ -74,7 +75,7 @@ static UIImage* noImage; #else // Rasterizing the cell layer increases rendering performance by quite a bit // but we want it unrasterized for tvOS where it must be scaled. - //self.layer.shouldRasterize = YES; + self.layer.shouldRasterize = YES; self.layer.rasterizationScale = [UIScreen mainScreen].scale; if (@available(iOS 13.4.1, *)) { @@ -136,7 +137,8 @@ static UIImage* noImage; BOOL noAppImage = false; // First check the memory cache - UIImage* appImage = [_artCache objectForKey:_app]; + //UIImage* appImage = [_artCache objectForKey:_app]; + UIImage* appImage = nil; // NSLog(@"appImage instance: %lu", (uintptr_t)appImage); if (appImage == nil) { // Next try to load from the on disk cache @@ -170,7 +172,7 @@ static UIImage* noImage; // Only create the app overlay if needed if (@available(iOS 13.0, *)) { - UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:23 weight:UIImageSymbolWeightBlack ]; + UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:23]; UIImageView* playIcon = [[UIImageView alloc] initWithImage:[[UIImage systemImageNamed:@"play.circle.fill" withConfiguration:config] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]]; //playIcon.tintColor = [[ThemeManager widgetBackgroundColor] colorWithAlphaComponent:0.85]; playIcon.tintColor = [[UIColor blackColor] colorWithAlphaComponent:0.55]; @@ -186,7 +188,7 @@ static UIImage* noImage; //_appOverlay.layer.shadowOffset = CGSizeMake(1, 1); //_appOverlay.layer.shadowOpacity = 1; //_appOverlay.layer.shadowRadius = 1.3; - _appOverlay.contentMode = UIViewContentModeScaleAspectFit; + //_appOverlay.contentMode = UIViewContentModeScaleAspectFit; } else if(noAppImage){ if (@available(iOS 13.0, *)) { @@ -195,18 +197,18 @@ static UIImage* noImage; UIImageView* appIcon = [[UIImageView alloc] initWithImage:[appIconImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]]; //layIcon.tintColor = [[ThemeManager widgetBackgroundColor] colorWithAlphaComponent:0.85]; - appIcon.tintColor = [[UIColor blackColor] colorWithAlphaComponent:0.55]; + appIcon.tintColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; _appOverlay = appIcon; } } - if (true) { + if(true) { _appLabel = [[UILabel alloc] init]; _appLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.55]; [_appLabel setTextColor:[[UIColor whiteColor] colorWithAlphaComponent:1]]; //_appLabel.shadowColor = [UIColor blackColor]; - [_appLabel setText:_app.name]; + [_appLabel setText:[_app.name isEqualToString:@"Steam Big Picture"] ? @"Steam" : _app.name]; [_appLabel setFont:[UIFont systemFontOfSize:15]]; [_appLabel setBaselineAdjustment:UIBaselineAdjustmentAlignCenters]; [_appLabel setTextAlignment:NSTextAlignmentCenter]; @@ -221,8 +223,8 @@ static UIImage* noImage; [_appImage.overlayContentView addSubview:_appLabel]; [_appImage.overlayContentView addSubview:_appOverlay]; #else - [self addSubview:_appLabel]; [self addSubview:_appOverlay]; + [self addSubview:_appLabel]; #endif } @@ -249,19 +251,16 @@ static UIImage* noImage; //[_appLabel setFrame:CGRectMake(padding, _appOverlay.frame.size.height + padding, frameSize.width - 2 * padding, frameSize.height - _appOverlay.frame.size.height - 2 * padding)]; } - /* - else if (_appOverlay != nil) { - _appOverlay.frame = CGRectMake(0, 0, frameSize.width / 2, frameSize.width / 2); - _appOverlay.center = center; - }*/ } - (void) updateLoop { // Stop immediately if the view has been detached - if (self.superview == nil) { + if ([self.updateLoopDelegate isStreaming] || self.superview == nil) { return; } + NSLog(@"appview update loop %f", CACurrentMediaTime()); + // Update the app image if neccessary if ((_appOverlay != nil && ![_app.id isEqualToString:_app.host.currentGame]) || (_appOverlay == nil && [_app.id isEqualToString:_app.host.currentGame])) { diff --git a/VoidLink/ViewControllers/MainFrameViewController.h b/VoidLink/ViewControllers/MainFrameViewController.h index 2f0d501e..ebc9b42e 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.h +++ b/VoidLink/ViewControllers/MainFrameViewController.h @@ -21,7 +21,7 @@ #import "HostCollectionViewController.h" -@interface MainFrameViewController : UICollectionViewController +@interface MainFrameViewController : UICollectionViewController @property (nonatomic, strong) IBOutlet UIBarButtonItem *settingsButton; #if !TARGET_OS_TV diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index d1a97e44..7d133c14 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -331,6 +331,7 @@ static NSMutableSet* hostList; // when at the host selection view. [self.navigationController.view removeGestureRecognizer:_menuRecognizer]; #endif + [self.collectionView setContentOffset:CGPointZero animated:NO]; [_appManager stopRetrieving]; _showHiddenApps = NO; _selectedHost = nil; @@ -1775,6 +1776,8 @@ static NSMutableSet* hostList; - (void)viewWillDisappear:(BOOL)animated{ NSLog(@"willDisappear"); [super viewWillDisappear:animated]; + [self.collectionView removeFromSuperview]; + [self.hostCollectionVC.view removeFromSuperview]; [_foregroundHostUpdateTimer invalidate]; _foregroundHostUpdateTimer = nil; } @@ -1811,6 +1814,7 @@ static NSMutableSet* hostList; [self retrieveSavedHosts]; _discMan = [[DiscoveryManager alloc] initWithHosts:[hostList allObjects] andCallback:self]; + } - (void)viewDidDisappear:(BOOL)animated @@ -2002,11 +2006,16 @@ static NSMutableSet* hostList; [self.collectionView reloadData]; } +- (bool)isStreaming{ + return self.revealViewController.isStreaming; +} + - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"AppCell" forIndexPath:indexPath]; TemporaryApp* app = _sortedAppList[indexPath.row]; UIAppView* appView = [[UIAppView alloc] initWithApp:app cache:_boxArtCache andCallback:self]; + appView.updateLoopDelegate = (id)self; if (appView.bounds.size.width > 10.0) { CGFloat scale = cell.bounds.size.width / appView.bounds.size.width; From ac66ffe302fd87066c7fe727c54620fb6c8be1f7 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sat, 19 Jul 2025 22:12:46 +0800 Subject: [PATCH 08/14] iOS 13 compatibility - hostIcon & lockIcon --- VoidLink/.DS_Store | Bin 10244 -> 14340 bytes VoidLink/HostCardView.m | 15 +++++++------ .../display.imageset/Contents.json | 21 ++++++++++++++++++ .../display.imageset/display.svg | 12 ++++++++++ .../lock.fill.imageset/Contents.json | 21 ++++++++++++++++++ .../lock.fill.imageset/lock.fill.svg | 11 +++++++++ 6 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 VoidLink/Images.xcassets/display.imageset/Contents.json create mode 100644 VoidLink/Images.xcassets/display.imageset/display.svg create mode 100644 VoidLink/Images.xcassets/lock.fill.imageset/Contents.json create mode 100644 VoidLink/Images.xcassets/lock.fill.imageset/lock.fill.svg diff --git a/VoidLink/.DS_Store b/VoidLink/.DS_Store index af91099b7496ebe7e7e723a5dc2273756f93404e..d5e91d03f49ced7b3311ebbaa10da5d91c509663 100644 GIT binary patch literal 14340 zcmZQzU|@7AO)+F(FkoO{;9!8z0z3>13~dYy3Lu&R0YLH~j1Ymcm_g<+Fff=gFfcGP zFbFU(6fl%Blz8UkCnx1V)!?I;p!!)D7#LU?JQ?zk_2(xsfQ@8IXJBBM_a6*EhB4h@ zU|^E{{~yF2#iJoG8UmvsFd71*Aut*OB!>VaLR8YIQQ$iM(i~6CSLO5!`5J0EvOLf@qLd5Dn7GzzAZ2%>Zj7rr=76;`FfxF(GcYoMwKFh+ZDwF#glK1AglK1Ag!WJvA=()jA=()jA=<%ajuN9G zFd71=As~m^_f9G=E=bA&_r13z735?VmlzmaV`O4xVP#|IVCUfGhz-ujFApwBEGaE^ zN-T;7@j~+RbCO`}#H6sy)be-%5$F88lElos)FP0Q;LMcNq{O0_@XWlF{Bo!K(!7*n zuwJMH2PX$-yntABwWX1vj)Fn0jzYDirKOI7nWb56EhmSlvc7dte0EN5UVaC}`LJSu zffvGulbxvkDGM&j%gN6JjUj*v4iN?)hD?TBhE#?ehD?TZh75)h23_#TLmERqgC0Wx zSiBfEh5-%-0R{%9tEgiT4A{*HU?^fpWk_SlWT*g}mCTR<84H1#!{Erkz{m$4%7Bbt zh+%P$9zz8~GD8YOB0~v7B10}iK0^xFy(wsBt*68+v~b~J@L|YjNM=X``@afocP2wV zLmsM~jIwBUvN7Z`Br;?&X2N^%K%AH#b6gWgW?06y3!enQQg5X3(cSW48aV^;LuKGZ~@z$ z%8(9@)l|%IX81*tZnQ9Fj6&1R#t_U<0?rAE=xKrhlxC5Ah@y5_lszauLd#`*3?U2| z45{Gq$Cn|IA&()EAsw0~&`Ju%U1*_-PcK@GFiD~5q4`O5tPf3bqRp&0;Sbl$Y?Iue>n3v<4*<#MpE>n*$>XI$oknCLct{qsC+MBs6@>t zpmH3=Hhy?`fLsj9v!F5|ham^-YV=$ND!tz!hqs4t_EKQ;@1S-N*d3BsbYp8x;WrVv#utXRRY9ge z+G0fX7D2ry20f6gpnW~!OhIc;f_f+@rtm`DLa-MFYCjXH6U|Og8yZC?7lRW+A_J(` zhqIpwYB!_k5n%v@7IK>#)B}XIW^uN^LG5R<%t7-5sAY{}4jY3rLlHwILjgkxLq2LN z92`?fD>@iB7+k=8(?o_Oh`-U)uSXkw!B9UeTG|-CLTg)dfcx{fQ-my9D2Xt*fk&cJ z8B!QRz-{jm22b#K4XDqFGev+$17wl%5wYf=r3mo20J1qO3~t~N4CwkG*q9hN^&_VS zJ_Zkle1>9%5(a06M1~@U6r?f) zGwL&1Fgh}NG5RouGR85+GZrwGGL|uxLrh=-jhKU&pg95%4aJP0nK%OJ9i{97jfjr+ z|DmBbN{xoVXb6mkz-S1JhQI)a04v0P1JL?ER|W=L>;ECDM#<3-7!84883K$fF2OEN z;6-`Z{RdiW2U<^;09A7Us*Dk|UJoJ$QUY4q2VRcH1ld$%0aXU37#Scd`4|}>EBOX& bAs}pD5onftwEvG-SvA`K2ldcLO#dGMpcY~x delta 203 zcmZoEXbF&DU|?W$DortDU{GLSVBlbY&;mRY3&ketNlrE}VUc8DU|>)KsbgReU|`5+ z$YID~NM%T7DA~MGB8q+D1JBLu94s7+YMbXMcrs5eRneF%sv65v7F?8RCBn + + + + + + + + + diff --git a/VoidLink/Images.xcassets/lock.fill.imageset/Contents.json b/VoidLink/Images.xcassets/lock.fill.imageset/Contents.json new file mode 100644 index 00000000..47a4b5de --- /dev/null +++ b/VoidLink/Images.xcassets/lock.fill.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "lock.fill.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/VoidLink/Images.xcassets/lock.fill.imageset/lock.fill.svg b/VoidLink/Images.xcassets/lock.fill.imageset/lock.fill.svg new file mode 100644 index 00000000..4f18cb6f --- /dev/null +++ b/VoidLink/Images.xcassets/lock.fill.imageset/lock.fill.svg @@ -0,0 +1,11 @@ + + + + + + + + + From 25c4ca01def93a496b970d16cefe26dd04f8f69b Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sat, 19 Jul 2025 23:00:20 +0800 Subject: [PATCH 09/14] hostView test --- VoidLink/ViewControllers/MainFrameViewController.m | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 7d133c14..afd95aad 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -331,7 +331,6 @@ static NSMutableSet* hostList; // when at the host selection view. [self.navigationController.view removeGestureRecognizer:_menuRecognizer]; #endif - [self.collectionView setContentOffset:CGPointZero animated:NO]; [_appManager stopRetrieving]; _showHiddenApps = NO; _selectedHost = nil; @@ -375,6 +374,8 @@ static NSMutableSet* hostList; self.collectionView.backgroundColor = [ThemeManager appBackgroundColor]; //self.view.backgroundColor = [ThemeManager appBackgroundColor]; + [self.collectionView setContentOffset:CGPointZero animated:NO]; + [self attachWaterMark]; self.navigationItem.rightBarButtonItems = @[_upButton]; self.revealViewController.mainFrameIsInHostView = false; @@ -1776,8 +1777,6 @@ static NSMutableSet* hostList; - (void)viewWillDisappear:(BOOL)animated{ NSLog(@"willDisappear"); [super viewWillDisappear:animated]; - [self.collectionView removeFromSuperview]; - [self.hostCollectionVC.view removeFromSuperview]; [_foregroundHostUpdateTimer invalidate]; _foregroundHostUpdateTimer = nil; } From f843acf12fb11a4ef7edae4e0755bf308a369ddc Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 20 Jul 2025 10:43:27 +0800 Subject: [PATCH 10/14] - hostView & host card fixes: add unpaired offline host to collection --- VoidLink/HostCardView.m | 14 ++++++++++---- .../ViewControllers/MainFrameViewController.m | 15 ++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/VoidLink/HostCardView.m b/VoidLink/HostCardView.m index b89c0ba5..3a1b1d0e 100644 --- a/VoidLink/HostCardView.m +++ b/VoidLink/HostCardView.m @@ -573,8 +573,12 @@ static const float REFRESH_CYCLE = 2.0f; } else { self.statusIcon.hidden = YES; } + + bool hostPaired = host.pairState == PairStatePaired; + _hostIconView.tintColor = [ThemeManager lowProfileGray]; - lockIconView.hidden = YES; + lockIconView.tintColor = [ThemeManager lowProfileGray]; + lockIconView.hidden = hostPaired; _appButton.hidden = YES; _launchButton.hidden = YES; _pairButton.hidden = YES; @@ -583,13 +587,15 @@ static const float REFRESH_CYCLE = 2.0f; if (@available(iOS 13.0, *)) { UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:buttonHeight/3.45 weight:UIImageSymbolWeightBold]; UIImage *templateImage = [UIImage systemImageNamed:@"power" withConfiguration:config]; - UIImage *coloredImage = [templateImage imageWithTintColor:defaultBlue renderingMode:UIImageRenderingModeAlwaysOriginal]; + UIImage *coloredImage = [templateImage imageWithTintColor:hostPaired ? defaultBlue : [ThemeManager textColorGray] renderingMode:UIImageRenderingModeAlwaysOriginal]; [self.wakeupButton setImage:coloredImage forState:UIControlStateNormal]; } else { // Fallback on earlier versions } - self.wakeupButton.backgroundColor = [ThemeManager textTintColorWithAlpha]; - [self.wakeupButton setTitleColor:defaultBlue forState:UIControlStateNormal]; + + self.wakeupButton.backgroundColor = hostPaired ? [ThemeManager textTintColorWithAlpha] : [[ThemeManager textColorGray] colorWithAlphaComponent:0.2]; + [self.wakeupButton setTitleColor: hostPaired ? defaultBlue : [ThemeManager textColorGray] forState:UIControlStateNormal]; + break; case StateUnknown: _hostSpinner.color = [UIColor whiteColor]; diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index afd95aad..ab79384f 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -1544,10 +1544,11 @@ static NSMutableSet* hostList; #endif [self updateTitle]; - // [self.view addSubview:hostScrollView]; - - // if ([hostList count] == 1) [self hostClicked:[hostList anyObject] view:nil]; // auto click for single host + [self retrieveSavedHosts]; + + _discMan = [[DiscoveryManager alloc] initWithHosts:[hostList allObjects] andCallback:self]; + //if([SettingsViewController isLandscapeNow] != _streamConfig.width > _streamConfig.height) //[self simulateSettingsButtonPress]; //force expand setting view if orientation changed since last quit from app. @@ -1809,11 +1810,6 @@ static NSMutableSet* hostList; [self.view addSubview:self.collectionView]; [self initHostCollection]; if(!_enteredAppView) [self switchToHostView]; - - [self retrieveSavedHosts]; - - _discMan = [[DiscoveryManager alloc] initWithHosts:[hostList allObjects] andCallback:self]; - } - (void)viewDidDisappear:(BOOL)animated @@ -1903,7 +1899,8 @@ static NSMutableSet* hostList; NSArray* sortedHostList = [[hostList allObjects] sortedArrayUsingSelector:@selector(compareName:)]; for (TemporaryHost* comp in sortedHostList) { - if(comp.state == StateOnline || comp.pairState == PairStatePaired || comp.pairState == PairStateUnknown) [self.hostCollectionVC addHost:comp]; + // if(comp.state == StateOnline || comp.pairState == PairStatePaired || comp.pairState == PairStateUnknown) [self.hostCollectionVC addHost:comp]; + [self.hostCollectionVC addHost:comp]; // Start jobs to decode the box art in advance for (TemporaryApp* app in comp.appList) { From 2b96bb1c77feb5b6b40270a5db57cb0017fee2ba Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 20 Jul 2025 11:37:48 +0800 Subject: [PATCH 11/14] appView background update tweak --- VoidLink/UIAppView.h | 2 +- VoidLink/UIAppView.m | 8 ++++++-- VoidLink/ViewControllers/MainFrameViewController.m | 13 +++++++------ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/VoidLink/UIAppView.h b/VoidLink/UIAppView.h index 9554d036..4bf31c45 100644 --- a/VoidLink/UIAppView.h +++ b/VoidLink/UIAppView.h @@ -10,7 +10,7 @@ #import "TemporaryApp.h" @protocol AppViewUpdateLoopDelegate -- (bool)isStreaming; +- (bool)isInAppView; @end diff --git a/VoidLink/UIAppView.m b/VoidLink/UIAppView.m index 71cfbfda..5975b34f 100644 --- a/VoidLink/UIAppView.m +++ b/VoidLink/UIAppView.m @@ -5,6 +5,10 @@ // Created by Diego Waxemberg on 10/22/14. // Copyright (c) 2014 Moonlight Stream. All rights reserved. // +// Created by True砖家 on 2025.7.20. +// Copyright © 2025 True砖家 @ Bilibili. All rights reserved. +// + #import "UIAppView.h" #import "AppAssetManager.h" @@ -255,7 +259,7 @@ static UIImage* noImage; - (void) updateLoop { // Stop immediately if the view has been detached - if ([self.updateLoopDelegate isStreaming] || self.superview == nil) { + if (self.superview == nil) { return; } @@ -277,7 +281,7 @@ static UIImage* noImage; [self setAlpha:_app.hidden ? 0.4 : 1.0]; // Queue the next refresh cycle - [self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE]; + if([self.updateLoopDelegate isInAppView]) [self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE]; } @end diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index ab79384f..6383fc2f 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -387,11 +387,12 @@ static NSMutableSet* hostList; //[self applyNavBarAppearance:navBarAppearance]; } -- (void)appButtonTappedForHost:(TemporaryHost *)host { +- (void)appButtonTappedForHost:(TemporaryHost *)host{ if (host.state != StateOnline) return; _selectedHost = host; - if (host.state == StateOnline && host.pairState == PairStatePaired && host.appList.count > 0) { - [self switchToAppView]; + if(host.state == StateOnline && host.pairState == PairStatePaired){ + [self updateAppsForHost:host]; + if(host.appList.count>0) [self switchToAppView]; } } @@ -492,7 +493,7 @@ static NSMutableSet* hostList; - (void) noneUserInitiatedHostAction:(TemporaryHost *)host view:(UIView *)view { - Log(LOG_D, @"Clicked host: %@", host.name); + NSLog(@"Clicked host: %@", host.name); _selectedHost = host; //_appManager = [[AppAssetManager alloc] initWithCallback:self]; [self.collectionView setCollectionViewLayout:self.collectionViewLayout]; @@ -2002,8 +2003,8 @@ static NSMutableSet* hostList; [self.collectionView reloadData]; } -- (bool)isStreaming{ - return self.revealViewController.isStreaming; +- (bool)isInAppView{ + return !self.revealViewController.isStreaming && _enteredAppView; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { From 11e50280204b8bb76ad1e5ca1deff9cb560633ef Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 20 Jul 2025 12:51:45 +0800 Subject: [PATCH 12/14] - save osw profile when read instruction button tapped - try to fix iOS 13 widgetPanel background --- VoidLink/Base.lproj/iPad.storyboard | 1 + VoidLink/Base.lproj/iPhone.storyboard | 11 ++++++----- .../LayoutOnScreenControlsViewController.m | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/VoidLink/Base.lproj/iPad.storyboard b/VoidLink/Base.lproj/iPad.storyboard index aadae408..4ec8402b 100644 --- a/VoidLink/Base.lproj/iPad.storyboard +++ b/VoidLink/Base.lproj/iPad.storyboard @@ -1506,6 +1506,7 @@ + diff --git a/VoidLink/Base.lproj/iPhone.storyboard b/VoidLink/Base.lproj/iPhone.storyboard index ed2ca41c..fc1c9a67 100644 --- a/VoidLink/Base.lproj/iPhone.storyboard +++ b/VoidLink/Base.lproj/iPhone.storyboard @@ -314,7 +314,7 @@ - + + @@ -1683,16 +1684,16 @@ - + - + - + - + diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index c3f58e51..35e425d4 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -370,6 +370,7 @@ UIAlertAction *readInstruction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Read Widget Instruction"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){ + [self saveTapped:nil]; NSURL *url = [NSURL URLWithString:@"https://b23.tv/J8qEXOr"]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; @@ -609,7 +610,7 @@ UIAlertController * savedAlertController = [UIAlertController alertControllerWithTitle: [NSString stringWithFormat:@""] message: [LocalizationHelper localizedStringForKey:@"Current profile updated successfully"] preferredStyle:UIAlertControllerStyleAlert]; [savedAlertController addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Ok"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { }]]; - [self presentViewController:savedAlertController animated:YES completion:nil]; + if(sender) [self presentViewController:savedAlertController animated:YES completion:nil]; } else{ UIAlertController * savedAlertController = [UIAlertController alertControllerWithTitle: [NSString stringWithFormat:@""] message: [LocalizationHelper localizedStringForKey:@"Profile Default can not be overwritten"] preferredStyle:UIAlertControllerStyleAlert]; From 0d3149a69637f14559d9c5d39a116ec631c8fb00 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 20 Jul 2025 14:11:37 +0800 Subject: [PATCH 13/14] ios 13 compatibility fix: disable auto & controller gyro mode --- VoidLink/ViewControllers/SettingsViewController.m | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 1d69fc7d..39555fd1 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -1300,6 +1300,12 @@ BOOL isCustomResolution(CGSize res) { [self.gyroSensitivitySlider addTarget:self action:@selector(gyroSensitivitySliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved. [self gyroSensitivitySliderMoved:self.gyroSensitivitySlider]; + if (@available(iOS 14.0, tvOS 14.0, *)) nil; + else{ + [self.gyroModeSelector setEnabled:false forSegmentAtIndex:1]; + [self.gyroModeSelector setEnabled:false forSegmentAtIndex:3]; + } + [self.emulatedControllerTypeSelector setSelectedSegmentIndex:[self controllerTypeToSegmentIndex:currentSettings.emulatedControllerType.intValue]]; [self.emulatedControllerTypeSelector addTarget:self action:@selector(emulatedControllerTypeChanged:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved. [self emulatedControllerTypeChanged:self.emulatedControllerTypeSelector]; From f22d05a34e1a18d765219427acf936e2f4e777ad Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 20 Jul 2025 16:22:33 +0800 Subject: [PATCH 14/14] - prevent onscreen widget moved out of superView bounds --- VoidLink/Input/OnScreenWidgetView.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index c3e34d09..2cea3abc 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -1134,10 +1134,15 @@ import UIKit let currentLocation: CGPoint if OnScreenWidgetView.editMode {currentLocation = touch.location(in: superview)} else {currentLocation = touch.location(in: self)} + + let offsetX = currentLocation.x - latestTouchLocation.x; + let offsetY = currentLocation.y - latestTouchLocation.y; + + let outOfBoundsX = center.x+offsetX > (self.superview?.bounds.width)! || center.x+offsetX < 0 + let outOfBoundsY = center.y+offsetY > (self.superview?.bounds.height)! || center.y+offsetY < 0 + + center = CGPoint(x: outOfBoundsX ? center.x : center.x+offsetX, y: outOfBoundsY ? center.y : center.y+offsetY) - let offsetX = currentLocation.x - latestTouchLocation.x - let offsetY = currentLocation.y - latestTouchLocation.y - center = CGPoint(x: center.x + offsetX, y: center.y + offsetY) latestTouchLocation = currentLocation // center = currentLocation; //NSLog("x coord: %f, y coord: %f", self.frame.origin.x, self.frame.origin.y)