diff --git a/Chinese Time.xcodeproj/project.pbxproj b/Chinendar.xcodeproj/project.pbxproj similarity index 80% rename from Chinese Time.xcodeproj/project.pbxproj rename to Chinendar.xcodeproj/project.pbxproj index 7fe0ede..4cb7fd3 100644 --- a/Chinese Time.xcodeproj/project.pbxproj +++ b/Chinendar.xcodeproj/project.pbxproj @@ -32,7 +32,7 @@ 9E90D7EF2A9EABD100855F2C /* Environments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E90D7EE2A9EABD100855F2C /* Environments.swift */; }; 9E90D7F02A9EABD100855F2C /* Environments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E90D7EE2A9EABD100855F2C /* Environments.swift */; }; 9E90D7F12A9EABD100855F2C /* Environments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E90D7EE2A9EABD100855F2C /* Environments.swift */; }; - 9E9889EF2A79EABF0066414A /* watchPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9889EE2A79EABF0066414A /* watchPanel.swift */; }; + 9E9889EF2A79EABF0066414A /* WatchPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9889EE2A79EABF0066414A /* WatchPanel.swift */; }; 9EBFBE332A58A40900DC42AF /* ThemeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBFBE322A58A40900DC42AF /* ThemeData.swift */; }; 9EBFBE342A58A40900DC42AF /* ThemeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBFBE322A58A40900DC42AF /* ThemeData.swift */; }; 9EBFBE352A58A40900DC42AF /* ThemeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBFBE322A58A40900DC42AF /* ThemeData.swift */; }; @@ -51,6 +51,8 @@ B301073D2A0999A900D0A50C /* SourceHanSansKR-Heavy.otf in Resources */ = {isa = PBXBuildFile; fileRef = B301073C2A0999A900D0A50C /* SourceHanSansKR-Heavy.otf */; }; B301073E2A0999A900D0A50C /* SourceHanSansKR-Heavy.otf in Resources */ = {isa = PBXBuildFile; fileRef = B301073C2A0999A900D0A50C /* SourceHanSansKR-Heavy.otf */; }; B30107402A09A0A500D0A50C /* SourceHanSansKR-Heavy.otf in Resources */ = {isa = PBXBuildFile; fileRef = B301073C2A0999A900D0A50C /* SourceHanSansKR-Heavy.otf */; }; + B30CF7642AF827A300B100CF /* visionApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B30CF7632AF827A300B100CF /* visionApp.swift */; }; + B30CF7682AF827A400B100CF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B30CF7672AF827A400B100CF /* Assets.xcassets */; }; B32243E22A0D3BF600E7AED5 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32243E12A0D3BF600E7AED5 /* Layout.swift */; }; B32243E32A0D3BF600E7AED5 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32243E12A0D3BF600E7AED5 /* Layout.swift */; }; B32243E52A0D8B6C00E7AED5 /* WatchWidgetBasic.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32243E42A0D8B6C00E7AED5 /* WatchWidgetBasic.swift */; }; @@ -80,6 +82,7 @@ B32999302A4F9C8600B71579 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32999282A4F9B7B00B71579 /* LocationManager.swift */; }; B32999312A4F9C8700B71579 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32999282A4F9B7B00B71579 /* LocationManager.swift */; }; B32999322A4F9C8700B71579 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32999282A4F9B7B00B71579 /* LocationManager.swift */; }; + B33635262B02FA7B00BA83F7 /* Setting.swift in Sources */ = {isa = PBXBuildFile; fileRef = B33635252B02FA7B00BA83F7 /* Setting.swift */; }; B34009472A352FEA003F50F7 /* WatchFaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E1D6E32A0ACD7800F2905A /* WatchFaceView.swift */; }; B34009482A352FEA003F50F7 /* WatchFaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E1D6E32A0ACD7800F2905A /* WatchFaceView.swift */; }; B34DA20929FDC0B200562449 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34DA20829FDC0B200562449 /* Utilities.swift */; }; @@ -92,9 +95,11 @@ B3515CFF29F6169D00E6BCDC /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2F0825C26FAB23500ADBE13 /* Data.swift */; }; B3515D0029F616A000E6BCDC /* PlanetModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CFF74C270FF940000CECDA /* PlanetModel.swift */; }; B3515D0129F616A200E6BCDC /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = D245D60826FA886200A89044 /* Model.swift */; }; + B356C9042B04460A0017EF03 /* WatchFace.swift in Sources */ = {isa = PBXBuildFile; fileRef = B356C9032B04460A0017EF03 /* WatchFace.swift */; }; B36D2F7A2A0483F800005162 /* WatchFaceBasics.swift in Sources */ = {isa = PBXBuildFile; fileRef = B36D2F782A047A2000005162 /* WatchFaceBasics.swift */; }; B37063A329FAFF3300CC6E57 /* MetaLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37063A229FAFF3300CC6E57 /* MetaLayout.swift */; }; B37063A429FAFF3300CC6E57 /* MetaLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37063A229FAFF3300CC6E57 /* MetaLayout.swift */; }; + B38387A92B08319500A04588 /* Welcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = B38387A82B08319500A04588 /* Welcome.swift */; }; B383A6CE2A4D02D8002FADCF /* Single.swift in Sources */ = {isa = PBXBuildFile; fileRef = B383A6CD2A4D02D8002FADCF /* Single.swift */; }; B383A6CF2A4D02D8002FADCF /* Single.swift in Sources */ = {isa = PBXBuildFile; fileRef = B383A6CD2A4D02D8002FADCF /* Single.swift */; }; B383A6D12A4D02E2002FADCF /* Dual.swift in Sources */ = {isa = PBXBuildFile; fileRef = B383A6D02A4D02E2002FADCF /* Dual.swift */; }; @@ -103,6 +108,8 @@ B383A6D62A4D1C7B002FADCF /* Relevance.swift in Sources */ = {isa = PBXBuildFile; fileRef = B383A6D32A4D1C7B002FADCF /* Relevance.swift */; }; B383A6D82A4D1EA1002FADCF /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = B383A6D72A4D1EA1002FADCF /* InfoPlist.xcstrings */; }; B383A6ED2A4D1EA2002FADCF /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = B383A6EC2A4D1EA2002FADCF /* Localizable.xcstrings */; }; + B38B52A22B14C5770055569E /* StatusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B38B52A12B14C5770055569E /* StatusState.swift */; }; + B38B52A32B14C5770055569E /* StatusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B38B52A12B14C5770055569E /* StatusState.swift */; }; B38CC0492A4F1F1600F4DB9F /* WatchFace.swift in Sources */ = {isa = PBXBuildFile; fileRef = B38CC0482A4F1F1600F4DB9F /* WatchFace.swift */; }; B38E96B12A0D3A82002FD662 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = D245D60826FA886200A89044 /* Model.swift */; }; B38E96B32A0D3A8A002FD662 /* PlanetModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CFF74C270FF940000CECDA /* PlanetModel.swift */; }; @@ -111,7 +118,7 @@ B39086012A0314DD00943F2B /* watchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39086002A0314DD00943F2B /* watchApp.swift */; }; B39086032A0314DD00943F2B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39086022A0314DD00943F2B /* ContentView.swift */; }; B39086052A0314DD00943F2B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B39086042A0314DD00943F2B /* Assets.xcassets */; }; - B390860B2A0314DD00943F2B /* Chinese Time.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = B39085FE2A0314DD00943F2B /* Chinese Time.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + B390860B2A0314DD00943F2B /* Chinendar.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = B39085FE2A0314DD00943F2B /* Chinendar.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; B39086122A0317CB00943F2B /* RoundedRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39086112A0317CB00943F2B /* RoundedRect.swift */; }; B39086132A0317CB00943F2B /* RoundedRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39086112A0317CB00943F2B /* RoundedRect.swift */; }; B39086142A0317CB00943F2B /* RoundedRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39086112A0317CB00943F2B /* RoundedRect.swift */; }; @@ -123,6 +130,30 @@ B395B5A62A0F1A4A003206E7 /* IconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B395B5A02A0ED7EF003206E7 /* IconView.swift */; }; B395B5A82A0F22CF003206E7 /* IconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B395B5A02A0ED7EF003206E7 /* IconView.swift */; }; B3970CF32A45066C0095F561 /* TextDesp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3CC8BF22A0C7E300063DE44 /* TextDesp.swift */; }; + B39B904F2B0178CC0083D05A /* HoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3F85EAC2A4A5A0B00F8B40B /* HoverView.swift */; }; + B39B90502B0178DF0083D05A /* ThemeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBFBE322A58A40900DC42AF /* ThemeData.swift */; }; + B39B90512B0178E90083D05A /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = D245D60826FA886200A89044 /* Model.swift */; }; + B39B90522B0178EC0083D05A /* PlanetModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CFF74C270FF940000CECDA /* PlanetModel.swift */; }; + B39B90532B0178EE0083D05A /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2F0825C26FAB23500ADBE13 /* Data.swift */; }; + B39B90542B0178F20083D05A /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32999282A4F9B7B00B71579 /* LocationManager.swift */; }; + B39B90552B0178F50083D05A /* MetaLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37063A229FAFF3300CC6E57 /* MetaLayout.swift */; }; + B39B90562B0179440083D05A /* WatchFaceBasics.swift in Sources */ = {isa = PBXBuildFile; fileRef = B36D2F782A047A2000005162 /* WatchFaceBasics.swift */; }; + B39B90572B0179470083D05A /* WatchFaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E1D6E32A0ACD7800F2905A /* WatchFaceView.swift */; }; + B39B90582B01794D0083D05A /* SwiftUIUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32243EF2A0DBEF400E7AED5 /* SwiftUIUtilities.swift */; }; + B39B90592B0179500083D05A /* RoundedRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39086112A0317CB00943F2B /* RoundedRect.swift */; }; + B39B905A2B0179580083D05A /* Environments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E90D7EE2A9EABD100855F2C /* Environments.swift */; }; + B39B905B2B0179680083D05A /* Locale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E0438C72A8FD5D7007217A8 /* Locale.swift */; }; + B39B905C2B0179700083D05A /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34DA20829FDC0B200562449 /* Utilities.swift */; }; + B39B905E2B0179B70083D05A /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39B905D2B0179B70083D05A /* Layout.swift */; }; + B39B90602B01803E0083D05A /* SourceHanSansKR-Heavy.otf in Resources */ = {isa = PBXBuildFile; fileRef = B301073C2A0999A900D0A50C /* SourceHanSansKR-Heavy.otf */; }; + B39B90622B01809A0083D05A /* layout.txt in Resources */ = {isa = PBXBuildFile; fileRef = B39B90612B01809A0083D05A /* layout.txt */; }; + B39B90642B0181CE0083D05A /* ThemesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B328A2EE2A3D19A4002191F4 /* ThemesList.swift */; }; + B39B90652B0181D20083D05A /* ColorSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = B328333B2A46687D00E36989 /* ColorSetting.swift */; }; + B39B90662B0181D50083D05A /* RingSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = B328333D2A4668B000E36989 /* RingSetting.swift */; }; + B39B90672B0181D70083D05A /* Documentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32833412A46691800E36989 /* Documentation.swift */; }; + B39B90682B0181DA0083D05A /* LayoutSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32833392A46685200E36989 /* LayoutSetting.swift */; }; + B39B90692B0181DE0083D05A /* Datetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32833432A46695000E36989 /* Datetime.swift */; }; + B39B906A2B0181E00083D05A /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32833452A4739FD00E36989 /* Location.swift */; }; B3BCCEE82A48746000F5745E /* Setting.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BCCEE72A48746000F5745E /* Setting.swift */; }; B3BEB4C32A48994C000751D5 /* WatchFace.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BEB4C22A48994C000751D5 /* WatchFace.swift */; }; B3BEB4C42A489A0A000751D5 /* WatchFaceBasics.swift in Sources */ = {isa = PBXBuildFile; fileRef = B36D2F782A047A2000005162 /* WatchFaceBasics.swift */; }; @@ -132,7 +163,7 @@ B3BFA2582A05E0590018F99E /* WatchConnectivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BFA2562A05E0590018F99E /* WatchConnectivity.swift */; }; B3CC8B9C2A0B30BB0063DE44 /* iOSWidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3CC8B9B2A0B30BB0063DE44 /* iOSWidgetBundle.swift */; }; B3CC8BA12A0B30BC0063DE44 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B3CC8BA02A0B30BC0063DE44 /* Assets.xcassets */; }; - B3CC8BA72A0B30BC0063DE44 /* Chinese Time Widget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = B3CC8B972A0B30BB0063DE44 /* Chinese Time Widget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + B3CC8BA72A0B30BC0063DE44 /* Chinendar Widget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = B3CC8B972A0B30BB0063DE44 /* Chinendar Widget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; B3CC8BAF2A0B31B10063DE44 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3515CE529F6149500E6BCDC /* Layout.swift */; }; B3CC8BB02A0B31B70063DE44 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = D245D60826FA886200A89044 /* Model.swift */; }; B3CC8BB12A0B31B90063DE44 /* PlanetModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CFF74C270FF940000CECDA /* PlanetModel.swift */; }; @@ -146,11 +177,11 @@ B3CC8BBD2A0B40E00063DE44 /* Full.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3CC8BB62A0B330C0063DE44 /* Full.swift */; }; B3CC8BF32A0C7E300063DE44 /* TextDesp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3CC8BF22A0C7E300063DE44 /* TextDesp.swift */; }; B3CC8BF62A0C7E310063DE44 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B3CC8BF52A0C7E310063DE44 /* Assets.xcassets */; }; - B3CC8BFC2A0C7E310063DE44 /* Chinese Time Widget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = B3CC8BEE2A0C7E300063DE44 /* Chinese Time Widget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + B3CC8BFC2A0C7E310063DE44 /* Chinendar Widget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = B3CC8BEE2A0C7E300063DE44 /* Chinendar Widget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; B3CE494F2A111971007905C1 /* SourceHanSansKR-Heavy.otf in Resources */ = {isa = PBXBuildFile; fileRef = B301073C2A0999A900D0A50C /* SourceHanSansKR-Heavy.otf */; }; B3E1D6CE2A0AB43E00F2905A /* MacWidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E1D6CD2A0AB43E00F2905A /* MacWidgetBundle.swift */; }; B3E1D6D12A0AB43E00F2905A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B3E1D6D02A0AB43E00F2905A /* Assets.xcassets */; }; - B3E1D6D82A0AB43E00F2905A /* Chinese Time Widget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = B3E1D6C62A0AB43E00F2905A /* Chinese Time Widget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + B3E1D6D82A0AB43E00F2905A /* Chinendar Widget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = B3E1D6C62A0AB43E00F2905A /* Chinendar Widget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; B3E1D6DD2A0AC88B00F2905A /* WatchFaceBasics.swift in Sources */ = {isa = PBXBuildFile; fileRef = B36D2F782A047A2000005162 /* WatchFaceBasics.swift */; }; B3E1D6DE2A0AC89300F2905A /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = D245D60826FA886200A89044 /* Model.swift */; }; B3E1D6DF2A0AC89600F2905A /* PlanetModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CFF74C270FF940000CECDA /* PlanetModel.swift */; }; @@ -160,6 +191,8 @@ B3E1D6E42A0ACD7800F2905A /* WatchFaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E1D6E32A0ACD7800F2905A /* WatchFaceView.swift */; }; B3E1D6E52A0ACDD800F2905A /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D26CF1BF26FD0C8D004EE9BB /* Layout.swift */; }; B3E1D6E82A0ADCF900F2905A /* layout.txt in Resources */ = {isa = PBXBuildFile; fileRef = B329909A296A1F7F00D246E9 /* layout.txt */; }; + B3E70DA82B01D7EF007474FB /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = B383A6EC2A4D1EA2002FADCF /* Localizable.xcstrings */; }; + B3E70DA92B01D7F4007474FB /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = B383A6D72A4D1EA1002FADCF /* InfoPlist.xcstrings */; }; B3E8A5162A4CF67700302473 /* Circular.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E8A5152A4CF67700302473 /* Circular.swift */; }; B3E8A5172A4CF67700302473 /* Circular.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E8A5152A4CF67700302473 /* Circular.swift */; }; B3E8A5192A4CF6BD00302473 /* CountDown.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E8A5182A4CF6BD00302473 /* CountDown.swift */; }; @@ -226,7 +259,7 @@ dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; dstSubfolderSpec = 16; files = ( - B390860B2A0314DD00943F2B /* Chinese Time.app in Embed Watch Content */, + B390860B2A0314DD00943F2B /* Chinendar.app in Embed Watch Content */, ); name = "Embed Watch Content"; runOnlyForDeploymentPostprocessing = 0; @@ -237,7 +270,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( - B3CC8BA72A0B30BC0063DE44 /* Chinese Time Widget.appex in Embed Foundation Extensions */, + B3CC8BA72A0B30BC0063DE44 /* Chinendar Widget.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; @@ -248,7 +281,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( - B3CC8BFC2A0C7E310063DE44 /* Chinese Time Widget.appex in Embed Foundation Extensions */, + B3CC8BFC2A0C7E310063DE44 /* Chinendar Widget.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; @@ -259,7 +292,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( - B3E1D6D82A0AB43E00F2905A /* Chinese Time Widget.appex in Embed Foundation Extensions */, + B3E1D6D82A0AB43E00F2905A /* Chinendar Widget.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; @@ -272,11 +305,15 @@ 9E71FD062A50BF2E00C9CA78 /* WatchFace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WatchFace.swift; path = macOS/Views/WatchFace.swift; sourceTree = SOURCE_ROOT; }; 9E820BE62A7DEA1700453389 /* Welcome.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Welcome.swift; sourceTree = ""; }; 9E90D7EE2A9EABD100855F2C /* Environments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Environments.swift; sourceTree = ""; }; - 9E9889EE2A79EABF0066414A /* watchPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = watchPanel.swift; sourceTree = ""; }; + 9E9889EE2A79EABF0066414A /* WatchPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchPanel.swift; sourceTree = ""; }; 9E9FB80D2A9BE02200888FA3 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; 9EBFBE322A58A40900DC42AF /* ThemeData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ThemeData.swift; path = Shared/DataModel/ThemeData.swift; sourceTree = SOURCE_ROOT; }; B301073C2A0999A900D0A50C /* SourceHanSansKR-Heavy.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceHanSansKR-Heavy.otf"; sourceTree = ""; }; B301073F2A099A0700D0A50C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B30CF75D2AF827A300B100CF /* Chinendar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Chinendar.app; sourceTree = BUILT_PRODUCTS_DIR; }; + B30CF7632AF827A300B100CF /* visionApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = visionApp.swift; sourceTree = ""; }; + B30CF7672AF827A400B100CF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + B30CF76C2AF827A400B100CF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B32243E12A0D3BF600E7AED5 /* Layout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Layout.swift; sourceTree = ""; }; B32243E42A0D8B6C00E7AED5 /* WatchWidgetBasic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchWidgetBasic.swift; sourceTree = ""; }; B32243E72A0D8E5000E7AED5 /* WatchWidgetBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchWidgetBundle.swift; sourceTree = ""; }; @@ -293,45 +330,52 @@ B329909A296A1F7F00D246E9 /* layout.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = layout.txt; sourceTree = ""; }; B32999212A4F96D600B71579 /* Setting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Setting.swift; sourceTree = ""; }; B32999282A4F9B7B00B71579 /* LocationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = ""; }; + B33635252B02FA7B00BA83F7 /* Setting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Setting.swift; path = Vision/Views/Setting.swift; sourceTree = SOURCE_ROOT; }; B34DA20829FDC0B200562449 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Utilities.swift; path = Shared/Utilities.swift; sourceTree = SOURCE_ROOT; }; - B3515CAB29F6147100E6BCDC /* Chinese Time.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Chinese Time.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + B3515CAB29F6147100E6BCDC /* Chinendar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Chinendar.app; sourceTree = BUILT_PRODUCTS_DIR; }; B3515CE329F6149500E6BCDC /* iOSApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; B3515CE529F6149500E6BCDC /* Layout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Layout.swift; sourceTree = ""; }; B3515CE629F6149500E6BCDC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B3515CE829F6149500E6BCDC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; B3515CFC29F6153E00E6BCDC /* layout.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = layout.txt; sourceTree = ""; }; - B3515D0629F6189F00E6BCDC /* ChineseTimeMac.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ChineseTimeMac.entitlements; sourceTree = ""; }; + B3515D0629F6189F00E6BCDC /* Chinendar.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Chinendar.entitlements; sourceTree = ""; }; + B356C9032B04460A0017EF03 /* WatchFace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchFace.swift; sourceTree = ""; }; B36D2F782A047A2000005162 /* WatchFaceBasics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchFaceBasics.swift; sourceTree = ""; }; B37063A229FAFF3300CC6E57 /* MetaLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetaLayout.swift; sourceTree = ""; }; + B38387A82B08319500A04588 /* Welcome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Welcome.swift; sourceTree = ""; }; B383A6CD2A4D02D8002FADCF /* Single.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Single.swift; sourceTree = ""; }; B383A6D02A4D02E2002FADCF /* Dual.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dual.swift; sourceTree = ""; }; B383A6D32A4D1C7B002FADCF /* Relevance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Relevance.swift; sourceTree = ""; }; B383A6D72A4D1EA1002FADCF /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = ""; }; B383A6EC2A4D1EA2002FADCF /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; + B38B52A12B14C5770055569E /* StatusState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = StatusState.swift; path = Shared/Setting/StatusState.swift; sourceTree = SOURCE_ROOT; }; B38CC0482A4F1F1600F4DB9F /* WatchFace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchFace.swift; sourceTree = ""; }; - B39085FE2A0314DD00943F2B /* Chinese Time.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Chinese Time.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + B39085FE2A0314DD00943F2B /* Chinendar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Chinendar.app; sourceTree = BUILT_PRODUCTS_DIR; }; B39086002A0314DD00943F2B /* watchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = watchApp.swift; sourceTree = ""; }; B39086022A0314DD00943F2B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; B39086042A0314DD00943F2B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; B39086112A0317CB00943F2B /* RoundedRect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedRect.swift; sourceTree = ""; }; B39086192A03522800943F2B /* layout.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = layout.txt; sourceTree = ""; }; B395B5A02A0ED7EF003206E7 /* IconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconView.swift; sourceTree = ""; }; + B39B905D2B0179B70083D05A /* Layout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Layout.swift; sourceTree = ""; }; + B39B905F2B017AB10083D05A /* Chinendar.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Chinendar.entitlements; sourceTree = ""; }; + B39B90612B01809A0083D05A /* layout.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = layout.txt; sourceTree = ""; }; B3BCCEE72A48746000F5745E /* Setting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Setting.swift; sourceTree = ""; }; B3BEB4C22A48994C000751D5 /* WatchFace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchFace.swift; sourceTree = ""; }; B3BFA2562A05E0590018F99E /* WatchConnectivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchConnectivity.swift; sourceTree = ""; }; - B3CC8B972A0B30BB0063DE44 /* Chinese Time Widget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Chinese Time Widget.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + B3CC8B972A0B30BB0063DE44 /* Chinendar Widget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Chinendar Widget.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; B3CC8B9B2A0B30BB0063DE44 /* iOSWidgetBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSWidgetBundle.swift; sourceTree = ""; }; B3CC8BA02A0B30BC0063DE44 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; B3CC8BA22A0B30BC0063DE44 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B3CC8BB62A0B330C0063DE44 /* Full.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Full.swift; sourceTree = ""; }; - B3CC8BC22A0BE4680063DE44 /* ChineseTimeiOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ChineseTimeiOS.entitlements; sourceTree = ""; }; + B3CC8BC22A0BE4680063DE44 /* Chinendar.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Chinendar.entitlements; sourceTree = ""; }; B3CC8BC32A0BE47D0063DE44 /* iOSWidget.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = iOSWidget.entitlements; sourceTree = ""; }; - B3CC8BC42A0BE48C0063DE44 /* ChineseTimeWatch.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ChineseTimeWatch.entitlements; sourceTree = ""; }; - B3CC8BEE2A0C7E300063DE44 /* Chinese Time Widget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Chinese Time Widget.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + B3CC8BC42A0BE48C0063DE44 /* Chinendar.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Chinendar.entitlements; sourceTree = ""; }; + B3CC8BEE2A0C7E300063DE44 /* Chinendar Widget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Chinendar Widget.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; B3CC8BF22A0C7E300063DE44 /* TextDesp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextDesp.swift; sourceTree = ""; }; B3CC8BF52A0C7E310063DE44 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; B3CC8BF72A0C7E310063DE44 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B3E1D6C62A0AB43E00F2905A /* Chinese Time Widget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Chinese Time Widget.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + B3E1D6C62A0AB43E00F2905A /* Chinendar Widget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Chinendar Widget.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; B3E1D6CD2A0AB43E00F2905A /* MacWidgetBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacWidgetBundle.swift; sourceTree = ""; }; B3E1D6D02A0AB43E00F2905A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; B3E1D6D22A0AB43E00F2905A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -346,7 +390,7 @@ D245D60826FA886200A89044 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; D26CF1BF26FD0C8D004EE9BB /* Layout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Layout.swift; sourceTree = ""; }; D2CFF74C270FF940000CECDA /* PlanetModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlanetModel.swift; sourceTree = ""; }; - D2E4E0E226F7C73E002F3716 /* Chinese Time.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Chinese Time.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + D2E4E0E226F7C73E002F3716 /* Chinendar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Chinendar.app; sourceTree = BUILT_PRODUCTS_DIR; }; D2E4E0E526F7C73E002F3716 /* macApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = macApp.swift; sourceTree = ""; }; D2E4E0EC26F7C73F002F3716 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; D2E4E0F126F7C73F002F3716 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -354,6 +398,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + B30CF75A2AF827A300B100CF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; B3515CA829F6147100E6BCDC /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -402,6 +453,7 @@ 9E910DDF2A525BCF009A1C54 /* Setting */ = { isa = PBXGroup; children = ( + B38B52A12B14C5770055569E /* StatusState.swift */, B328A2EE2A3D19A4002191F4 /* ThemesList.swift */, B328333B2A46687D00E36989 /* ColorSetting.swift */, B328333D2A4668B000E36989 /* RingSetting.swift */, @@ -413,6 +465,20 @@ path = Setting; sourceTree = ""; }; + B30CF75E2AF827A300B100CF /* Vision */ = { + isa = PBXGroup; + children = ( + B356C9052B0450220017EF03 /* Views */, + B30CF7632AF827A300B100CF /* visionApp.swift */, + B39B905D2B0179B70083D05A /* Layout.swift */, + B30CF76C2AF827A400B100CF /* Info.plist */, + B39B90612B01809A0083D05A /* layout.txt */, + B39B905F2B017AB10083D05A /* Chinendar.entitlements */, + B30CF7672AF827A400B100CF /* Assets.xcassets */, + ); + path = Vision; + sourceTree = ""; + }; B32833382A46681E00E36989 /* Views */ = { isa = PBXGroup; children = ( @@ -466,7 +532,7 @@ B3515CE529F6149500E6BCDC /* Layout.swift */, B3515CE629F6149500E6BCDC /* Info.plist */, B3515CFC29F6153E00E6BCDC /* layout.txt */, - B3CC8BC22A0BE4680063DE44 /* ChineseTimeiOS.entitlements */, + B3CC8BC22A0BE4680063DE44 /* Chinendar.entitlements */, B3515CE829F6149500E6BCDC /* Assets.xcassets */, ); path = iOS; @@ -489,6 +555,16 @@ path = Shared; sourceTree = ""; }; + B356C9052B0450220017EF03 /* Views */ = { + isa = PBXGroup; + children = ( + B356C9032B04460A0017EF03 /* WatchFace.swift */, + B33635252B02FA7B00BA83F7 /* Setting.swift */, + B38387A82B08319500A04588 /* Welcome.swift */, + ); + path = Views; + sourceTree = ""; + }; B39085FF2A0314DD00943F2B /* Watch */ = { isa = PBXGroup; children = ( @@ -497,7 +573,7 @@ B32243E12A0D3BF600E7AED5 /* Layout.swift */, B301073F2A099A0700D0A50C /* Info.plist */, B39086192A03522800943F2B /* layout.txt */, - B3CC8BC42A0BE48C0063DE44 /* ChineseTimeWatch.entitlements */, + B3CC8BC42A0BE48C0063DE44 /* Chinendar.entitlements */, B39086042A0314DD00943F2B /* Assets.xcassets */, ); path = Watch; @@ -581,6 +657,7 @@ D2E4E0E426F7C73E002F3716 /* macOS */, B3515CAC29F6147100E6BCDC /* iOS */, B39085FF2A0314DD00943F2B /* Watch */, + B30CF75E2AF827A300B100CF /* Vision */, B3CC8BBE2A0B42990063DE44 /* Widget */, B3E1D6CC2A0AB43E00F2905A /* MacWidget */, B3CC8B9A2A0B30BB0063DE44 /* iOSWidget */, @@ -592,12 +669,13 @@ D2E4E0E326F7C73E002F3716 /* Products */ = { isa = PBXGroup; children = ( - D2E4E0E226F7C73E002F3716 /* Chinese Time.app */, - B3515CAB29F6147100E6BCDC /* Chinese Time.app */, - B39085FE2A0314DD00943F2B /* Chinese Time.app */, - B3E1D6C62A0AB43E00F2905A /* Chinese Time Widget.appex */, - B3CC8B972A0B30BB0063DE44 /* Chinese Time Widget.appex */, - B3CC8BEE2A0C7E300063DE44 /* Chinese Time Widget.appex */, + D2E4E0E226F7C73E002F3716 /* Chinendar.app */, + B3515CAB29F6147100E6BCDC /* Chinendar.app */, + B39085FE2A0314DD00943F2B /* Chinendar.app */, + B3E1D6C62A0AB43E00F2905A /* Chinendar Widget.appex */, + B3CC8B972A0B30BB0063DE44 /* Chinendar Widget.appex */, + B3CC8BEE2A0C7E300063DE44 /* Chinendar Widget.appex */, + B30CF75D2AF827A300B100CF /* Chinendar.app */, ); name = Products; sourceTree = ""; @@ -607,11 +685,11 @@ children = ( B32999332A4F9E1700B71579 /* Views */, D2E4E0E526F7C73E002F3716 /* macApp.swift */, - 9E9889EE2A79EABF0066414A /* watchPanel.swift */, + 9E9889EE2A79EABF0066414A /* WatchPanel.swift */, D26CF1BF26FD0C8D004EE9BB /* Layout.swift */, B329909A296A1F7F00D246E9 /* layout.txt */, D2E4E0F126F7C73F002F3716 /* Info.plist */, - B3515D0629F6189F00E6BCDC /* ChineseTimeMac.entitlements */, + B3515D0629F6189F00E6BCDC /* Chinendar.entitlements */, D2E4E0EC26F7C73F002F3716 /* Assets.xcassets */, ); path = macOS; @@ -620,9 +698,28 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - B3515CAA29F6147100E6BCDC /* Chinese Time iOS */ = { + B30CF75C2AF827A300B100CF /* Chinendar Vision */ = { + isa = PBXNativeTarget; + buildConfigurationList = B30CF76F2AF827A400B100CF /* Build configuration list for PBXNativeTarget "Chinendar Vision" */; + buildPhases = ( + B30CF7592AF827A300B100CF /* Sources */, + B30CF75A2AF827A300B100CF /* Frameworks */, + B30CF75B2AF827A300B100CF /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Chinendar Vision"; + packageProductDependencies = ( + ); + productName = "ChineseTime-Vision"; + productReference = B30CF75D2AF827A300B100CF /* Chinendar.app */; + productType = "com.apple.product-type.application"; + }; + B3515CAA29F6147100E6BCDC /* Chinendar iOS */ = { isa = PBXNativeTarget; - buildConfigurationList = B3515CD529F6147200E6BCDC /* Build configuration list for PBXNativeTarget "Chinese Time iOS" */; + buildConfigurationList = B3515CD529F6147200E6BCDC /* Build configuration list for PBXNativeTarget "Chinendar iOS" */; buildPhases = ( B3515CA729F6147100E6BCDC /* Sources */, B3515CA829F6147100E6BCDC /* Frameworks */, @@ -636,14 +733,14 @@ B390860A2A0314DD00943F2B /* PBXTargetDependency */, B3CC8BA62A0B30BC0063DE44 /* PBXTargetDependency */, ); - name = "Chinese Time iOS"; + name = "Chinendar iOS"; productName = "Chinese Time iOS"; - productReference = B3515CAB29F6147100E6BCDC /* Chinese Time.app */; + productReference = B3515CAB29F6147100E6BCDC /* Chinendar.app */; productType = "com.apple.product-type.application"; }; - B39085FD2A0314DD00943F2B /* Chinese Time Watch */ = { + B39085FD2A0314DD00943F2B /* Chinendar Watch */ = { isa = PBXNativeTarget; - buildConfigurationList = B390860F2A0314DD00943F2B /* Build configuration list for PBXNativeTarget "Chinese Time Watch" */; + buildConfigurationList = B390860F2A0314DD00943F2B /* Build configuration list for PBXNativeTarget "Chinendar Watch" */; buildPhases = ( B39085FA2A0314DD00943F2B /* Sources */, B39085FB2A0314DD00943F2B /* Frameworks */, @@ -655,9 +752,9 @@ dependencies = ( B3CC8BFB2A0C7E310063DE44 /* PBXTargetDependency */, ); - name = "Chinese Time Watch"; + name = "Chinendar Watch"; productName = "ChineseTime Watch App"; - productReference = B39085FE2A0314DD00943F2B /* Chinese Time.app */; + productReference = B39085FE2A0314DD00943F2B /* Chinendar.app */; productType = "com.apple.product-type.application"; }; B3CC8B962A0B30BB0063DE44 /* iOS Widget Extension */ = { @@ -674,7 +771,7 @@ ); name = "iOS Widget Extension"; productName = iOSWidgetExtension; - productReference = B3CC8B972A0B30BB0063DE44 /* Chinese Time Widget.appex */; + productReference = B3CC8B972A0B30BB0063DE44 /* Chinendar Widget.appex */; productType = "com.apple.product-type.app-extension"; }; B3CC8BED2A0C7E300063DE44 /* Watch Widget Extension */ = { @@ -691,7 +788,7 @@ ); name = "Watch Widget Extension"; productName = WatchWidgetExtension; - productReference = B3CC8BEE2A0C7E300063DE44 /* Chinese Time Widget.appex */; + productReference = B3CC8BEE2A0C7E300063DE44 /* Chinendar Widget.appex */; productType = "com.apple.product-type.app-extension"; }; B3E1D6C52A0AB43E00F2905A /* Mac Widget Extension */ = { @@ -708,12 +805,12 @@ ); name = "Mac Widget Extension"; productName = MacWidgetExtension; - productReference = B3E1D6C62A0AB43E00F2905A /* Chinese Time Widget.appex */; + productReference = B3E1D6C62A0AB43E00F2905A /* Chinendar Widget.appex */; productType = "com.apple.product-type.app-extension"; }; - D2E4E0E126F7C73E002F3716 /* Chinese Time Mac */ = { + D2E4E0E126F7C73E002F3716 /* Chinendar Mac */ = { isa = PBXNativeTarget; - buildConfigurationList = D2E4E0F526F7C73F002F3716 /* Build configuration list for PBXNativeTarget "Chinese Time Mac" */; + buildConfigurationList = D2E4E0F526F7C73F002F3716 /* Build configuration list for PBXNativeTarget "Chinendar Mac" */; buildPhases = ( D2E4E0DE26F7C73E002F3716 /* Sources */, D2E4E0DF26F7C73E002F3716 /* Frameworks */, @@ -726,11 +823,11 @@ dependencies = ( B3E1D6D72A0AB43E00F2905A /* PBXTargetDependency */, ); - name = "Chinese Time Mac"; + name = "Chinendar Mac"; packageProductDependencies = ( ); productName = ChineseTime; - productReference = D2E4E0E226F7C73E002F3716 /* Chinese Time.app */; + productReference = D2E4E0E226F7C73E002F3716 /* Chinendar.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -740,9 +837,12 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastSwiftUpdateCheck = 1430; - LastUpgradeCheck = 1500; + LastSwiftUpdateCheck = 1510; + LastUpgradeCheck = 1510; TargetAttributes = { + B30CF75C2AF827A300B100CF = { + CreatedOnToolsVersion = 15.1; + }; B3515CAA29F6147100E6BCDC = { CreatedOnToolsVersion = 14.3; LastSwiftMigration = 1430; @@ -764,7 +864,7 @@ }; }; }; - buildConfigurationList = D2E4E0DD26F7C73E002F3716 /* Build configuration list for PBXProject "Chinese Time" */; + buildConfigurationList = D2E4E0DD26F7C73E002F3716 /* Build configuration list for PBXProject "Chinendar" */; compatibilityVersion = "Xcode 15.0"; developmentRegion = "zh-Hant"; hasScannedForEncodings = 0; @@ -780,9 +880,10 @@ projectDirPath = ""; projectRoot = ""; targets = ( - D2E4E0E126F7C73E002F3716 /* Chinese Time Mac */, - B3515CAA29F6147100E6BCDC /* Chinese Time iOS */, - B39085FD2A0314DD00943F2B /* Chinese Time Watch */, + D2E4E0E126F7C73E002F3716 /* Chinendar Mac */, + B3515CAA29F6147100E6BCDC /* Chinendar iOS */, + B39085FD2A0314DD00943F2B /* Chinendar Watch */, + B30CF75C2AF827A300B100CF /* Chinendar Vision */, B3E1D6C52A0AB43E00F2905A /* Mac Widget Extension */, B3CC8B962A0B30BB0063DE44 /* iOS Widget Extension */, B3CC8BED2A0C7E300063DE44 /* Watch Widget Extension */, @@ -791,6 +892,18 @@ /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + B30CF75B2AF827A300B100CF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B3E70DA92B01D7F4007474FB /* InfoPlist.xcstrings in Resources */, + B3E70DA82B01D7EF007474FB /* Localizable.xcstrings in Resources */, + B39B90602B01803E0083D05A /* SourceHanSansKR-Heavy.otf in Resources */, + B39B90622B01809A0083D05A /* layout.txt in Resources */, + B30CF7682AF827A400B100CF /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; B3515CA929F6147100E6BCDC /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -866,6 +979,40 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + B30CF7592AF827A300B100CF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B39B90552B0178F50083D05A /* MetaLayout.swift in Sources */, + B39B90562B0179440083D05A /* WatchFaceBasics.swift in Sources */, + B33635262B02FA7B00BA83F7 /* Setting.swift in Sources */, + B39B90662B0181D50083D05A /* RingSetting.swift in Sources */, + B39B905A2B0179580083D05A /* Environments.swift in Sources */, + B39B90572B0179470083D05A /* WatchFaceView.swift in Sources */, + B38387A92B08319500A04588 /* Welcome.swift in Sources */, + B39B906A2B0181E00083D05A /* Location.swift in Sources */, + B39B905E2B0179B70083D05A /* Layout.swift in Sources */, + B39B90652B0181D20083D05A /* ColorSetting.swift in Sources */, + B39B90502B0178DF0083D05A /* ThemeData.swift in Sources */, + B39B90582B01794D0083D05A /* SwiftUIUtilities.swift in Sources */, + B39B90692B0181DE0083D05A /* Datetime.swift in Sources */, + B39B90682B0181DA0083D05A /* LayoutSetting.swift in Sources */, + B39B905B2B0179680083D05A /* Locale.swift in Sources */, + B39B904F2B0178CC0083D05A /* HoverView.swift in Sources */, + B38B52A32B14C5770055569E /* StatusState.swift in Sources */, + B39B90522B0178EC0083D05A /* PlanetModel.swift in Sources */, + B39B905C2B0179700083D05A /* Utilities.swift in Sources */, + B39B90512B0178E90083D05A /* Model.swift in Sources */, + B39B90642B0181CE0083D05A /* ThemesList.swift in Sources */, + B30CF7642AF827A300B100CF /* visionApp.swift in Sources */, + B39B90672B0181D70083D05A /* Documentation.swift in Sources */, + B39B90592B0179500083D05A /* RoundedRect.swift in Sources */, + B39B90532B0178EE0083D05A /* Data.swift in Sources */, + B356C9042B04460A0017EF03 /* WatchFace.swift in Sources */, + B39B90542B0178F20083D05A /* LocationManager.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; B3515CA729F6147100E6BCDC /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1022,11 +1169,12 @@ B32999222A4F96D600B71579 /* Setting.swift in Sources */, 9E0438C82A8FD5D7007217A8 /* Locale.swift in Sources */, B32999292A4F9B7B00B71579 /* LocationManager.swift in Sources */, + B38B52A22B14C5770055569E /* StatusState.swift in Sources */, 9ECDCA022A50B24800E11161 /* LayoutSetting.swift in Sources */, 9E71FD072A50BF2E00C9CA78 /* WatchFace.swift in Sources */, D245D60926FA886200A89044 /* Model.swift in Sources */, D26CF1C026FD0C8D004EE9BB /* Layout.swift in Sources */, - 9E9889EF2A79EABF0066414A /* watchPanel.swift in Sources */, + 9E9889EF2A79EABF0066414A /* WatchPanel.swift in Sources */, B34DA20929FDC0B200562449 /* Utilities.swift in Sources */, 9ECDCA052A50B6D100E11161 /* Documentation.swift in Sources */, 9E90D7EF2A9EABD100855F2C /* Environments.swift in Sources */, @@ -1048,7 +1196,7 @@ /* Begin PBXTargetDependency section */ B390860A2A0314DD00943F2B /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = B39085FD2A0314DD00943F2B /* Chinese Time Watch */; + target = B39085FD2A0314DD00943F2B /* Chinendar Watch */; targetProxy = B39086092A0314DD00943F2B /* PBXContainerItemProxy */; }; B3CC8BA62A0B30BC0063DE44 /* PBXTargetDependency */ = { @@ -1069,18 +1217,94 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + B30CF76D2AF827A400B100CF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = vision/Chinendar.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = 28HU5A7B46; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = vision/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Chinendar; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_NSHumanReadableCopyright = "Open source under GPL v3"; + INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; + INFOPLIST_KEY_NSLocationUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = "$(APP_VERSION)"; + PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime"; + PRODUCT_NAME = Chinendar; + SDKROOT = xros; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 7; + XROS_DEPLOYMENT_TARGET = 1.0; + }; + name = Debug; + }; + B30CF76E2AF827A400B100CF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = vision/Chinendar.entitlements; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = 28HU5A7B46; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = vision/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Chinendar; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_NSHumanReadableCopyright = "Open source under GPL v3"; + INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; + INFOPLIST_KEY_NSLocationUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = "$(APP_VERSION)"; + PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime"; + PRODUCT_NAME = Chinendar; + SDKROOT = xros; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 7; + VALIDATE_PRODUCT = YES; + XROS_DEPLOYMENT_TARGET = 1.0; + }; + name = Release; + }; B3515CD629F6147200E6BCDC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; - CODE_SIGN_ENTITLEMENTS = iOS/ChineseTimeiOS.entitlements; + CODE_SIGN_ENTITLEMENTS = iOS/Chinendar.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; DEVELOPMENT_TEAM = 28HU5A7B46; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = iOS/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Chinese Time"; + INFOPLIST_KEY_CFBundleDisplayName = Chinendar; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; INFOPLIST_KEY_NSHumanReadableCopyright = "Open source under GPL v3"; INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; @@ -1095,9 +1319,8 @@ ); MARKETING_VERSION = "$(APP_VERSION)"; PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime"; - PRODUCT_NAME = "Chinese Time"; + PRODUCT_NAME = Chinendar; SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -1110,15 +1333,16 @@ B3515CD729F6147200E6BCDC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; - CODE_SIGN_ENTITLEMENTS = iOS/ChineseTimeiOS.entitlements; + CODE_SIGN_ENTITLEMENTS = iOS/Chinendar.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; DEVELOPMENT_TEAM = 28HU5A7B46; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = iOS/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Chinese Time"; + INFOPLIST_KEY_CFBundleDisplayName = Chinendar; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; INFOPLIST_KEY_NSHumanReadableCopyright = "Open source under GPL v3"; INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; @@ -1133,9 +1357,8 @@ ); MARKETING_VERSION = "$(APP_VERSION)"; PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime"; - PRODUCT_NAME = "Chinese Time"; + PRODUCT_NAME = Chinendar; SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -1149,17 +1372,17 @@ B390860D2A0314DD00943F2B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = Watch/ChineseTimeWatch.entitlements; + CODE_SIGN_ENTITLEMENTS = Watch/Chinendar.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = $APP_VERSION; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 28HU5A7B46; - ENABLE_PREVIEWS = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Watch/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Chinese Time"; + INFOPLIST_KEY_CFBundleDisplayName = Chinendar; INFOPLIST_KEY_NSHumanReadableCopyright = "Open source under GPL v3"; INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; INFOPLIST_KEY_NSLocationUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; @@ -1174,7 +1397,7 @@ MARKETING_VERSION = $APP_BUILD; OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime.Watch"; - PRODUCT_NAME = "Chinese Time"; + PRODUCT_NAME = Chinendar; SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -1187,17 +1410,17 @@ B390860E2A0314DD00943F2B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = Watch/ChineseTimeWatch.entitlements; + CODE_SIGN_ENTITLEMENTS = Watch/Chinendar.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = $APP_VERSION; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 28HU5A7B46; - ENABLE_PREVIEWS = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Watch/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Chinese Time"; + INFOPLIST_KEY_CFBundleDisplayName = Chinendar; INFOPLIST_KEY_NSHumanReadableCopyright = "Open source under GPL v3"; INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; INFOPLIST_KEY_NSLocationUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; @@ -1212,7 +1435,7 @@ MARKETING_VERSION = $APP_BUILD; OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime.Watch"; - PRODUCT_NAME = "Chinese Time"; + PRODUCT_NAME = Chinendar; SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -1226,6 +1449,7 @@ B3CC8BA92A0B30BC0063DE44 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = iOSWidget/iOSWidget.entitlements; @@ -1234,7 +1458,7 @@ DEVELOPMENT_TEAM = 28HU5A7B46; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = iOSWidget/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Chinese Time Widget"; + INFOPLIST_KEY_CFBundleDisplayName = "Chinendar Widget"; INFOPLIST_KEY_NSHumanReadableCopyright = "Open source under GPL v3"; INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; INFOPLIST_KEY_NSLocationUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; @@ -1248,10 +1472,9 @@ MARKETING_VERSION = $APP_VERSION; OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime.iOSWidget"; - PRODUCT_NAME = "Chinese Time Widget"; + PRODUCT_NAME = "Chinendar Widget"; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -1264,6 +1487,7 @@ B3CC8BAA2A0B30BC0063DE44 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = iOSWidget/iOSWidget.entitlements; @@ -1272,7 +1496,7 @@ DEVELOPMENT_TEAM = 28HU5A7B46; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = iOSWidget/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Chinese Time Widget"; + INFOPLIST_KEY_CFBundleDisplayName = "Chinendar Widget"; INFOPLIST_KEY_NSHumanReadableCopyright = "Open source under GPL v3"; INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; INFOPLIST_KEY_NSLocationUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; @@ -1286,10 +1510,9 @@ MARKETING_VERSION = $APP_VERSION; OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime.iOSWidget"; - PRODUCT_NAME = "Chinese Time Widget"; + PRODUCT_NAME = "Chinendar Widget"; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -1303,6 +1526,7 @@ B3CC8BFE2A0C7E310063DE44 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = WatchWidget/WatchWidget.entitlements; @@ -1311,7 +1535,7 @@ DEVELOPMENT_TEAM = 28HU5A7B46; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WatchWidget/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Chinese Time Widget"; + INFOPLIST_KEY_CFBundleDisplayName = "Chinendar Widget"; INFOPLIST_KEY_NSHumanReadableCopyright = "Open source under GPL v3"; INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; INFOPLIST_KEY_NSLocationUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; @@ -1324,7 +1548,7 @@ ); MARKETING_VERSION = $APP_BUILD; PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime.Watch.WatchWidget"; - PRODUCT_NAME = "Chinese Time Widget"; + PRODUCT_NAME = "Chinendar Widget"; SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -1337,6 +1561,7 @@ B3CC8BFF2A0C7E310063DE44 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = WatchWidget/WatchWidget.entitlements; @@ -1345,7 +1570,7 @@ DEVELOPMENT_TEAM = 28HU5A7B46; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WatchWidget/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Chinese Time Widget"; + INFOPLIST_KEY_CFBundleDisplayName = "Chinendar Widget"; INFOPLIST_KEY_NSHumanReadableCopyright = "Open source under GPL v3"; INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; INFOPLIST_KEY_NSLocationUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; @@ -1358,7 +1583,7 @@ ); MARKETING_VERSION = $APP_BUILD; PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime.Watch.WatchWidget"; - PRODUCT_NAME = "Chinese Time Widget"; + PRODUCT_NAME = "Chinendar Widget"; SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -1372,6 +1597,7 @@ B3E1D6DA2A0AB43E00F2905A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = MacWidget/MacWidget.entitlements; @@ -1380,7 +1606,7 @@ DEVELOPMENT_TEAM = 28HU5A7B46; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = MacWidget/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Chinese Time Widget"; + INFOPLIST_KEY_CFBundleDisplayName = "Chinendar Widget"; INFOPLIST_KEY_NSHumanReadableCopyright = "Open source under GPL v3"; INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; INFOPLIST_KEY_NSLocationUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; @@ -1394,7 +1620,7 @@ MARKETING_VERSION = $APP_VERSION; OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime.MacWidget"; - PRODUCT_NAME = "Chinese Time Widget"; + PRODUCT_NAME = "Chinendar Widget"; SKIP_INSTALL = YES; STRINGS_FILE_OUTPUT_ENCODING = binary; SWIFT_EMIT_LOC_STRINGS = YES; @@ -1405,6 +1631,7 @@ B3E1D6DB2A0AB43E00F2905A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = MacWidget/MacWidget.entitlements; @@ -1414,7 +1641,7 @@ ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = MacWidget/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Chinese Time Widget"; + INFOPLIST_KEY_CFBundleDisplayName = "Chinendar Widget"; INFOPLIST_KEY_NSHumanReadableCopyright = "Open source under GPL v3"; INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; INFOPLIST_KEY_NSLocationUsageDescription = "Provide location to calculate local sunrise/set, moonrise/set times. No impact on other functions if you refuse."; @@ -1428,11 +1655,12 @@ MARKETING_VERSION = $APP_VERSION; OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime.MacWidget"; - PRODUCT_NAME = "Chinese Time Widget"; + PRODUCT_NAME = "Chinendar Widget"; SKIP_INSTALL = YES; STRINGS_FILE_OUTPUT_ENCODING = binary; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; + VALIDATE_PRODUCT = YES; }; name = Release; }; @@ -1440,8 +1668,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 97; - APP_VERSION = 5.0.1; + APP_BUILD = 103; + APP_VERSION = 5.1; ASSETCATALOG_COMPILER_APPICON_NAME = ""; ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOL_FRAMEWORKS = SwiftUI; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -1506,13 +1734,12 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; STRINGS_FILE_OUTPUT_ENCODING = binary; - STRIP_INSTALLED_PRODUCT = NO; - STRIP_SWIFT_SYMBOLS = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; WATCHOS_DEPLOYMENT_TARGET = 10.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Debug; }; @@ -1520,8 +1747,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 97; - APP_VERSION = 5.0.1; + APP_BUILD = 103; + APP_VERSION = 5.1; ASSETCATALOG_COMPILER_APPICON_NAME = ""; ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOL_FRAMEWORKS = SwiftUI; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -1587,22 +1814,24 @@ SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; WATCHOS_DEPLOYMENT_TARGET = 10.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Release; }; D2E4E0F626F7C73F002F3716 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = macOS/ChineseTimeMac.entitlements; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; + CODE_SIGN_ENTITLEMENTS = macOS/Chinendar.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; + CURRENT_PROJECT_VERSION = 102; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 28HU5A7B46; INFOPLIST_FILE = macOS/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Chinese Time"; + INFOPLIST_KEY_CFBundleDisplayName = Chinendar; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -1611,7 +1840,7 @@ MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = "$(APP_VERSION)"; PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime"; - PRODUCT_NAME = "Chinese Time"; + PRODUCT_NAME = Chinendar; STRINGS_FILE_OUTPUT_ENCODING = binary; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -1621,18 +1850,19 @@ D2E4E0F726F7C73F002F3716 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = macOS/ChineseTimeMac.entitlements; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; + CODE_SIGN_ENTITLEMENTS = macOS/Chinendar.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; + CURRENT_PROJECT_VERSION = 102; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 28HU5A7B46; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = macOS/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "Chinese Time"; + INFOPLIST_KEY_CFBundleDisplayName = Chinendar; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -1641,17 +1871,27 @@ MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = "$(APP_VERSION)"; PRODUCT_BUNDLE_IDENTIFIER = "Yuncao-Liu.ChineseTime"; - PRODUCT_NAME = "Chinese Time"; + PRODUCT_NAME = Chinendar; STRINGS_FILE_OUTPUT_ENCODING = binary; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; + VALIDATE_PRODUCT = YES; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - B3515CD529F6147200E6BCDC /* Build configuration list for PBXNativeTarget "Chinese Time iOS" */ = { + B30CF76F2AF827A400B100CF /* Build configuration list for PBXNativeTarget "Chinendar Vision" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B30CF76D2AF827A400B100CF /* Debug */, + B30CF76E2AF827A400B100CF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B3515CD529F6147200E6BCDC /* Build configuration list for PBXNativeTarget "Chinendar iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( B3515CD629F6147200E6BCDC /* Debug */, @@ -1660,7 +1900,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - B390860F2A0314DD00943F2B /* Build configuration list for PBXNativeTarget "Chinese Time Watch" */ = { + B390860F2A0314DD00943F2B /* Build configuration list for PBXNativeTarget "Chinendar Watch" */ = { isa = XCConfigurationList; buildConfigurations = ( B390860D2A0314DD00943F2B /* Debug */, @@ -1696,7 +1936,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D2E4E0DD26F7C73E002F3716 /* Build configuration list for PBXProject "Chinese Time" */ = { + D2E4E0DD26F7C73E002F3716 /* Build configuration list for PBXProject "Chinendar" */ = { isa = XCConfigurationList; buildConfigurations = ( D2E4E0F326F7C73F002F3716 /* Debug */, @@ -1705,7 +1945,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D2E4E0F526F7C73F002F3716 /* Build configuration list for PBXNativeTarget "Chinese Time Mac" */ = { + D2E4E0F526F7C73F002F3716 /* Build configuration list for PBXNativeTarget "Chinendar Mac" */ = { isa = XCConfigurationList; buildConfigurations = ( D2E4E0F626F7C73F002F3716 /* Debug */, diff --git a/Chinese Time.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Chinendar.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 74% rename from Chinese Time.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Chinendar.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 4a501ad..8bd332f 100644 --- a/Chinese Time.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/Chinendar.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:/Users/leo/Library/Mobile Documents/com~apple~CloudDocs/Codes/ChineseTime/Chinendar.xcodeproj"> diff --git a/Chinese Time.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Chinendar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Chinese Time.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Chinendar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Chinese Time.xcodeproj/xcshareddata/xcschemes/Chinese Time Mac.xcscheme b/Chinendar.xcodeproj/xcshareddata/xcschemes/Chinendar Mac.xcscheme similarity index 86% rename from Chinese Time.xcodeproj/xcshareddata/xcschemes/Chinese Time Mac.xcscheme rename to Chinendar.xcodeproj/xcshareddata/xcschemes/Chinendar Mac.xcscheme index 610c7d7..0ddb8e5 100644 --- a/Chinese Time.xcodeproj/xcshareddata/xcschemes/Chinese Time Mac.xcscheme +++ b/Chinendar.xcodeproj/xcshareddata/xcschemes/Chinendar Mac.xcscheme @@ -1,6 +1,6 @@ + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar Mac" + ReferencedContainer = "container:Chinendar.xcodeproj"> @@ -47,9 +47,9 @@ + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar Mac" + ReferencedContainer = "container:Chinendar.xcodeproj"> @@ -86,9 +86,9 @@ + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar Mac" + ReferencedContainer = "container:Chinendar.xcodeproj"> diff --git a/Chinendar.xcodeproj/xcshareddata/xcschemes/Chinendar Vision.xcscheme b/Chinendar.xcodeproj/xcshareddata/xcschemes/Chinendar Vision.xcscheme new file mode 100644 index 0000000..eb6eb5c --- /dev/null +++ b/Chinendar.xcodeproj/xcshareddata/xcschemes/Chinendar Vision.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Chinese Time.xcodeproj/xcshareddata/xcschemes/Chinese Time Watch.xcscheme b/Chinendar.xcodeproj/xcshareddata/xcschemes/Chinendar Watch.xcscheme similarity index 83% rename from Chinese Time.xcodeproj/xcshareddata/xcschemes/Chinese Time Watch.xcscheme rename to Chinendar.xcodeproj/xcshareddata/xcschemes/Chinendar Watch.xcscheme index a69a103..505bcef 100644 --- a/Chinese Time.xcodeproj/xcshareddata/xcschemes/Chinese Time Watch.xcscheme +++ b/Chinendar.xcodeproj/xcshareddata/xcschemes/Chinendar Watch.xcscheme @@ -1,6 +1,6 @@ + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar Watch" + ReferencedContainer = "container:Chinendar.xcodeproj"> + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar iOS" + ReferencedContainer = "container:Chinendar.xcodeproj"> @@ -59,9 +59,9 @@ + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar Watch" + ReferencedContainer = "container:Chinendar.xcodeproj"> @@ -98,9 +98,9 @@ + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar Watch" + ReferencedContainer = "container:Chinendar.xcodeproj"> diff --git a/Chinese Time.xcodeproj/xcshareddata/xcschemes/Chinese Time iOS.xcscheme b/Chinendar.xcodeproj/xcshareddata/xcschemes/Chinendar iOS.xcscheme similarity index 85% rename from Chinese Time.xcodeproj/xcshareddata/xcschemes/Chinese Time iOS.xcscheme rename to Chinendar.xcodeproj/xcshareddata/xcschemes/Chinendar iOS.xcscheme index 3af2ebd..601280c 100644 --- a/Chinese Time.xcodeproj/xcshareddata/xcschemes/Chinese Time iOS.xcscheme +++ b/Chinendar.xcodeproj/xcshareddata/xcschemes/Chinendar iOS.xcscheme @@ -1,6 +1,6 @@ + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar iOS" + ReferencedContainer = "container:Chinendar.xcodeproj"> @@ -44,9 +44,9 @@ + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar iOS" + ReferencedContainer = "container:Chinendar.xcodeproj"> @@ -83,9 +83,9 @@ + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar iOS" + ReferencedContainer = "container:Chinendar.xcodeproj"> diff --git a/Chinese Time.xcodeproj/xcshareddata/xcschemes/Mac Widget Extension.xcscheme b/Chinendar.xcodeproj/xcshareddata/xcschemes/Mac Widget Extension.xcscheme similarity index 86% rename from Chinese Time.xcodeproj/xcshareddata/xcschemes/Mac Widget Extension.xcscheme rename to Chinendar.xcodeproj/xcshareddata/xcschemes/Mac Widget Extension.xcscheme index 6aa8d59..6f8a08b 100644 --- a/Chinese Time.xcodeproj/xcshareddata/xcschemes/Mac Widget Extension.xcscheme +++ b/Chinendar.xcodeproj/xcshareddata/xcschemes/Mac Widget Extension.xcscheme @@ -1,6 +1,6 @@ + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar Mac" + ReferencedContainer = "container:Chinendar.xcodeproj"> + ReferencedContainer = "container:Chinendar.xcodeproj"> @@ -65,9 +65,9 @@ + ReferencedContainer = "container:Chinendar.xcodeproj"> @@ -105,9 +105,9 @@ + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar Mac" + ReferencedContainer = "container:Chinendar.xcodeproj"> diff --git a/Chinese Time.xcodeproj/xcshareddata/xcschemes/Watch Widget Extension.xcscheme b/Chinendar.xcodeproj/xcshareddata/xcschemes/Watch Widget Extension.xcscheme similarity index 82% rename from Chinese Time.xcodeproj/xcshareddata/xcschemes/Watch Widget Extension.xcscheme rename to Chinendar.xcodeproj/xcshareddata/xcschemes/Watch Widget Extension.xcscheme index e026fec..2e0fcc9 100644 --- a/Chinese Time.xcodeproj/xcshareddata/xcschemes/Watch Widget Extension.xcscheme +++ b/Chinendar.xcodeproj/xcshareddata/xcschemes/Watch Widget Extension.xcscheme @@ -1,6 +1,6 @@ + ReferencedContainer = "container:Chinendar.xcodeproj"> + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar Watch" + ReferencedContainer = "container:Chinendar.xcodeproj"> + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar iOS" + ReferencedContainer = "container:Chinendar.xcodeproj"> @@ -74,18 +74,18 @@ + ReferencedContainer = "container:Chinendar.xcodeproj"> + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar iOS" + ReferencedContainer = "container:Chinendar.xcodeproj"> @@ -117,9 +117,9 @@ + ReferencedContainer = "container:Chinendar.xcodeproj"> diff --git a/Chinese Time.xcodeproj/xcshareddata/xcschemes/iOS Widget Extension.xcscheme b/Chinendar.xcodeproj/xcshareddata/xcschemes/iOS Widget Extension.xcscheme similarity index 83% rename from Chinese Time.xcodeproj/xcshareddata/xcschemes/iOS Widget Extension.xcscheme rename to Chinendar.xcodeproj/xcshareddata/xcschemes/iOS Widget Extension.xcscheme index e617cfc..d1cee05 100644 --- a/Chinese Time.xcodeproj/xcshareddata/xcschemes/iOS Widget Extension.xcscheme +++ b/Chinendar.xcodeproj/xcshareddata/xcschemes/iOS Widget Extension.xcscheme @@ -1,6 +1,6 @@ + ReferencedContainer = "container:Chinendar.xcodeproj"> + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar iOS" + ReferencedContainer = "container:Chinendar.xcodeproj"> @@ -62,18 +62,18 @@ + ReferencedContainer = "container:Chinendar.xcodeproj"> + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar iOS" + ReferencedContainer = "container:Chinendar.xcodeproj"> @@ -107,9 +107,9 @@ + BuildableName = "Chinendar.app" + BlueprintName = "Chinendar iOS" + ReferencedContainer = "container:Chinendar.xcodeproj"> diff --git a/Chinese Time.xcodeproj/xcuserdata/leo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Chinendar.xcodeproj/xcuserdata/leo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist similarity index 100% rename from Chinese Time.xcodeproj/xcuserdata/leo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist rename to Chinendar.xcodeproj/xcuserdata/leo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist diff --git a/Chinese Time.xcodeproj/xcuserdata/leo.xcuserdatad/xcschemes/xcschememanagement.plist b/Chinendar.xcodeproj/xcuserdata/leo.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 86% rename from Chinese Time.xcodeproj/xcuserdata/leo.xcuserdatad/xcschemes/xcschememanagement.plist rename to Chinendar.xcodeproj/xcuserdata/leo.xcuserdatad/xcschemes/xcschememanagement.plist index fa8e9e7..c28be17 100644 --- a/Chinese Time.xcodeproj/xcuserdata/leo.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Chinendar.xcodeproj/xcuserdata/leo.xcuserdatad/xcschemes/xcschememanagement.plist @@ -4,17 +4,22 @@ SchemeUserState - Chinese Time Mac.xcscheme_^#shared#^_ + Chinendar Mac.xcscheme_^#shared#^_ orderHint 0 - Chinese Time Watch.xcscheme_^#shared#^_ + Chinendar Vision.xcscheme_^#shared#^_ + + orderHint + 3 + + Chinendar Watch.xcscheme_^#shared#^_ orderHint 2 - Chinese Time iOS.xcscheme_^#shared#^_ + Chinendar iOS.xcscheme_^#shared#^_ orderHint 1 @@ -32,12 +37,12 @@ Mac Widget Extension.xcscheme_^#shared#^_ orderHint - 3 + 4 Watch Widget Extension.xcscheme_^#shared#^_ orderHint - 5 + 6 WidgetExtension.xcscheme_^#shared#^_ @@ -47,7 +52,7 @@ iOS Widget Extension.xcscheme_^#shared#^_ orderHint - 4 + 5 SuppressBuildableAutocreation diff --git a/Chinese Time.xcodeproj/xcuserdata/leoliu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Chinendar.xcodeproj/xcuserdata/leoliu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist similarity index 100% rename from Chinese Time.xcodeproj/xcuserdata/leoliu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist rename to Chinendar.xcodeproj/xcuserdata/leoliu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist diff --git a/Chinese Time.xcodeproj/xcuserdata/leoliu.xcuserdatad/xcschemes/xcschememanagement.plist b/Chinendar.xcodeproj/xcuserdata/leoliu.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 100% rename from Chinese Time.xcodeproj/xcuserdata/leoliu.xcuserdatad/xcschemes/xcschememanagement.plist rename to Chinendar.xcodeproj/xcuserdata/leoliu.xcuserdatad/xcschemes/xcschememanagement.plist diff --git a/Chinese Time.xcodeproj/xcuserdata/leoyoontsaw.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Chinendar.xcodeproj/xcuserdata/leoyoontsaw.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist similarity index 100% rename from Chinese Time.xcodeproj/xcuserdata/leoyoontsaw.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist rename to Chinendar.xcodeproj/xcuserdata/leoyoontsaw.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist diff --git a/Chinese Time.xcodeproj/xcuserdata/leoyoontsaw.xcuserdatad/xcschemes/xcschememanagement.plist b/Chinendar.xcodeproj/xcuserdata/leoyoontsaw.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 100% rename from Chinese Time.xcodeproj/xcuserdata/leoyoontsaw.xcuserdatad/xcschemes/xcschememanagement.plist rename to Chinendar.xcodeproj/xcuserdata/leoyoontsaw.xcuserdatad/xcschemes/xcschememanagement.plist diff --git a/README.md b/README.md index 7e1f53e..15c7639 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

Lunar Time華曆/Chinese Time/Lunar Time

+

Chinendar華曆/Chinendar

Download on the App Store @@ -6,9 +6,9 @@ For detailed information about the background, design (screenshots), and features of this app, please refer to the [Wiki](https://github.com/LEOYoon-Tsaw/ChineseTime/wiki) page. Additionally, the app includes a documentation section that serves as a Q&A resource. -This app is available on `macOS`, `iOS`, `iPadOS`, and `watchOS`, and includes widgets for all these platforms. To build the project from the source, simply download or clone this repo, then build with Xcode. The app does not rely on any third-party dependencies. +This app is available on `macOS`, `iOS`, `iPadOS`, `watchOS` and `visionOS`, and includes widgets for all these platforms but visionOS. To build the project from the source, simply download or clone this repo, then build with Xcode. The app does not rely on any third-party dependencies. -Minimum OS requirements: `macOS 14.0`, `iOS/iPadOS 17.0`, `watchOS 10.0`. You will need `Xcode 15.0` or later to build from the source. +Minimum OS requirements: `macOS 14.0`, `iOS/iPadOS 17.0`, `watchOS 10.0`, `visionOS 1.0`. You will need `Xcode 15.2` or later to build from the source. ## Contributing diff --git a/Shared/DataModel/Data.swift b/Shared/DataModel/Data.swift index 6627aad..d108884 100644 --- a/Shared/DataModel/Data.swift +++ b/Shared/DataModel/Data.swift @@ -1,8 +1,8 @@ // // Data.swift -// ChineseTime +// Chinendar // -// Created by LEO Yoon-Tsaw on 9/21/21. +// Created by Leo Liu on 9/21/21. // let earthSpeed = 360 / 86164.0989 diff --git a/Shared/DataModel/MetaLayout.swift b/Shared/DataModel/MetaLayout.swift index 5c6ff9a..dda618e 100644 --- a/Shared/DataModel/MetaLayout.swift +++ b/Shared/DataModel/MetaLayout.swift @@ -1,6 +1,6 @@ // -// Layout.swift -// Chinese Time +// MetaLayout.swift +// Chinendar // // Created by Leo Liu on 4/27/23. // @@ -142,7 +142,7 @@ extension String { init(locations: [CGFloat], colors: [CGColor], loop: Bool) { guard locations.count == colors.count else { - fatalError() + fatalError("Gradient locations count does not equal colors count") } var colorAndLocation = Array(zip(colors, locations)) colorAndLocation.sort { former, latter in @@ -231,6 +231,7 @@ extension String { var secondRing = Gradient(locations: [0, 1], colors: [CGColor(gray: 1, alpha: 1), CGColor(gray: 1, alpha: 1)], loop: false) var thirdRing = Gradient(locations: [0, 1], colors: [CGColor(gray: 1, alpha: 1), CGColor(gray: 1, alpha: 1)], loop: false) var innerColor = CGColor(gray: 0, alpha: 0) + var backColor = CGColor(gray: 1, alpha: 1) var majorTickColor = CGColor(gray: 0, alpha: 0) var minorTickColor = CGColor(gray: 0, alpha: 0) var majorTickAlpha: CGFloat = 0 @@ -240,6 +241,7 @@ extension String { var evenSolarTermTickColor = CGColor(gray: 1, alpha: 1) var oddSolarTermTickColor = CGColor(gray: 0.67, alpha: 1) var innerColorDark = CGColor(gray: 0, alpha: 0) + var backColorDark = CGColor(gray: 0, alpha: 1) var majorTickColorDark = CGColor(gray: 0, alpha: 0) var minorTickColorDark = CGColor(gray: 0, alpha: 0) var fontColorDark = CGColor(gray: 0, alpha: 1) @@ -276,6 +278,7 @@ extension String { encoded += "secondRing: \(secondRing.encode().replacingOccurrences(of: "\n", with: "; "))\n" encoded += "thirdRing: \(thirdRing.encode().replacingOccurrences(of: "\n", with: "; "))\n" encoded += "innerColor: \(innerColor.hexCode)\n" + encoded += "backColor: \(backColor.hexCode)\n" encoded += "majorTickColor: \(majorTickColor.hexCode)\n" encoded += "majorTickAlpha: \(majorTickAlpha)\n" encoded += "minorTickColor: \(minorTickColor.hexCode)\n" @@ -285,6 +288,7 @@ extension String { encoded += "evenSolarTermTickColor: \(evenSolarTermTickColor.hexCode)\n" encoded += "oddSolarTermTickColor: \(oddSolarTermTickColor.hexCode)\n" encoded += "innerColorDark: \(innerColorDark.hexCode)\n" + encoded += "backColorDark: \(backColorDark.hexCode)\n" encoded += "majorTickColorDark: \(majorTickColorDark.hexCode)\n" encoded += "minorTickColorDark: \(minorTickColorDark.hexCode)\n" encoded += "fontColorDark: \(fontColorDark.hexCode)\n" @@ -323,7 +327,7 @@ extension String { return values } - func update(from values: [String: String]) { + func update(from values: [String: String], updateSize: Bool = true) { let seperatorRegex = /(\s*;|\{\})/ func readGradient(value: String?) -> Gradient? { guard let value = value else { return nil } @@ -353,6 +357,7 @@ extension String { secondRing = readGradient(value: values["secondRing"]) ?? secondRing thirdRing = readGradient(value: values["thirdRing"]) ?? thirdRing innerColor = values["innerColor"]?.colorValue ?? innerColor + backColor = values["backColor"]?.colorValue ?? backColor majorTickColor = values["majorTickColor"]?.colorValue ?? majorTickColor majorTickAlpha = values["majorTickAlpha"]?.floatValue ?? majorTickAlpha minorTickColor = values["minorTickColor"]?.colorValue ?? minorTickColor @@ -361,10 +366,11 @@ extension String { centerFontColor = readGradient(value: values["centerFontColor"]) ?? centerFontColor evenSolarTermTickColor = values["evenSolarTermTickColor"]?.colorValue ?? evenSolarTermTickColor oddSolarTermTickColor = values["oddSolarTermTickColor"]?.colorValue ?? oddSolarTermTickColor - innerColorDark = values["innerColorDark"]?.colorValue ?? innerColor - majorTickColorDark = values["majorTickColorDark"]?.colorValue ?? majorTickColor - minorTickColorDark = values["minorTickColorDark"]?.colorValue ?? minorTickColor - fontColorDark = values["fontColorDark"]?.colorValue ?? fontColor + innerColorDark = values["innerColorDark"]?.colorValue ?? innerColorDark + backColorDark = values["backColorDark"]?.colorValue ?? backColorDark + majorTickColorDark = values["majorTickColorDark"]?.colorValue ?? majorTickColorDark + minorTickColorDark = values["minorTickColorDark"]?.colorValue ?? minorTickColorDark + fontColorDark = values["fontColorDark"]?.colorValue ?? fontColorDark evenSolarTermTickColorDark = values["evenSolarTermTickColorDark"]?.colorValue ?? evenSolarTermTickColorDark oddSolarTermTickColorDark = values["oddSolarTermTickColorDark"]?.colorValue ?? oddSolarTermTickColorDark if let colourList = readColorList(values["planetIndicator"]), colourList.count == self.planetIndicator.count { @@ -386,15 +392,17 @@ extension String { centerTextHOffset = values["centerTextHorizontalOffset"]?.floatValue ?? centerTextHOffset verticalTextOffset = values["verticalTextOffset"]?.floatValue ?? verticalTextOffset horizontalTextOffset = values["horizontalTextOffset"]?.floatValue ?? horizontalTextOffset - if let width = values["watchWidth"]?.floatValue, let height = values["watchHeight"]?.floatValue { - watchSize = CGSize(width: width, height: height) + if updateSize { + if let width = values["watchWidth"]?.floatValue, let height = values["watchHeight"]?.floatValue { + watchSize = CGSize(width: width, height: height) + } } cornerRadiusRatio = values["cornerRadiusRatio"]?.floatValue ?? cornerRadiusRatio } - func update(from str: String) { + func update(from str: String, updateSize: Bool = true) { let values = extract(from: str) - update(from: values) + update(from: values, updateSize: updateSize) } func loadStatic() { @@ -417,9 +425,9 @@ extension String { }() let descriptor = FetchDescriptor(predicate: predicate, sortBy: [SortDescriptor(\.modifiedDate, order: .reverse)]) + var found = false do { let themes = try context.fetch(descriptor) - var found = false for theme in themes { if !found && !theme.isNil { self.update(from: theme.code!) @@ -427,27 +435,29 @@ extension String { break } } - if !found { - let filePath = Bundle.main.path(forResource: "layout", ofType: "txt")! - let defaultLayout = try! String(contentsOfFile: filePath) - self.update(from: defaultLayout) - } } catch { - fatalError(error.localizedDescription) + print(error.localizedDescription) + } + + if !found { + let filePath = Bundle.main.path(forResource: "layout", ofType: "txt")! + let defaultLayout = try! String(contentsOfFile: filePath) + self.update(from: defaultLayout) } } func saveDefault(context: ModelContext) { let defaultName = ThemeData.defaultName let deviceName = ThemeData.deviceName + try? LocalData.write(deviceName: deviceName) + let predicate = #Predicate { data in data.name == defaultName && data.deviceName == deviceName } let descriptor = FetchDescriptor(predicate: predicate, sortBy: [SortDescriptor(\.modifiedDate, order: .reverse)]) + var found = false do { - try LocalData.write(deviceName: deviceName) let themes = try context.fetch(descriptor) - var found = false for theme in themes { if !found && !theme.isNil { theme.update(code: self.encode()) @@ -456,12 +466,13 @@ extension String { context.delete(theme) } } - if !found { - let defaultTheme = ThemeData(name: ThemeData.defaultName, code: self.encode()) - context.insert(defaultTheme) - } } catch { - fatalError(error.localizedDescription) + print(error.localizedDescription) + } + + if !found { + let defaultTheme = ThemeData(name: ThemeData.defaultName, code: self.encode()) + context.insert(defaultTheme) } } } diff --git a/Shared/DataModel/Model.swift b/Shared/DataModel/Model.swift index 4c70569..45efd00 100644 --- a/Shared/DataModel/Model.swift +++ b/Shared/DataModel/Model.swift @@ -1,8 +1,8 @@ // // Model.swift -// ChineseTime +// Chinendar // -// Created by LEO Yoon-Tsaw on 9/21/21. +// Created by Leo Liu on 9/21/21. // import Foundation @@ -445,9 +445,20 @@ extension Array { } @Observable final class ChineseCalendar { + + struct ChineseDate: Hashable { + var month: Int + var day: Int + var leap: Bool = false + + static func == (lhs: ChineseDate, rhs: ChineseDate) -> Bool { + lhs.month == rhs.month && lhs.day == rhs.day && lhs.leap == rhs.leap + } + } + static let updateInterval: CGFloat = 14.4 //Seconds - static let month_chinese = ["冬月", "臘月", "正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月"] - static let month_chinese_compact = ["㋊", "㋋", "㋀", "㋁", "㋂", "㋃", "㋄", "㋅", "㋆", "㋇", "㋈", "㋉"] + static let month_chinese = ["正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "冬月", "臘月", ] + static let month_chinese_compact = ["㋀", "㋁", "㋂", "㋃", "㋄", "㋅", "㋆", "㋇", "㋈", "㋉", "㋊", "㋋"] static let day_chinese = ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十", "廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"] static let day_chinese_compact = ["㏠", "㏡", "㏢", "㏣", "㏤", "㏥", "㏦", "㏧", "㏨", "㏩", "㏪", "㏫", "㏬", "㏭", "㏮", "㏯", "㏰", "㏱", "㏲", "㏳", "㏴", "㏵", "㏶", "㏷", "㏸", "㏹", "㏺", "㏻", "㏼", "㏽"] static let terrestrial_branches = ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"] @@ -460,7 +471,10 @@ extension Array { static let dayTimeName = ["夜中", "日出", "日中", "日入"] static let moonTimeName = ["月出", "月中", "月入"] static let moonPhases = ["朔", "望"] - static let holidays = ["正月一日": "元旦", "正月十五": "上元", "三月三日": "上巳", "五月五日": "端午", "七月七日": "七夕", "七月十五": "中元", "九月九日": "重陽", "八月十五": "中秋", "十月十五": "下元", "大年三十": "除夕"] + static let holidays = [ChineseDate(month: 1, day: 1): "元旦", ChineseDate(month: 1, day: 15): "上元", ChineseDate(month: 2, day: 2): "春社", + ChineseDate(month: 3, day: 3): "上巳", ChineseDate(month: 5, day: 5): "端午", ChineseDate(month: 7, day: 7): "七夕", + ChineseDate(month: 7, day: 15): "中元", ChineseDate(month: 9, day: 9): "重陽", ChineseDate(month: 8, day: 15): "中秋", + ChineseDate(month: 10, day: 15): "下元", ChineseDate(month: 12, day: 8): "臘祭", ChineseDate(month: 12, day: -1): "除夕"] static let start: Date = { var components = DateComponents() components.year = 1901 @@ -485,16 +499,16 @@ extension Array { private var _calendar: Calendar = .utcCalendar private var _location: CGPoint? = nil @ObservationIgnored private var _year: Int = 0 + @ObservationIgnored private var _numberOfMonths: Int = 0 @ObservationIgnored private var _year_length: Double = 0 @ObservationIgnored private var _solarTerms: [Date] = [] @ObservationIgnored private var _evenSolarTerms: [Date] = [] @ObservationIgnored private var _oddSolarTerms: [Date] = [] @ObservationIgnored private var _moonEclipses: [Date] = [] @ObservationIgnored private var _fullMoons: [Date] = [] - @ObservationIgnored private var _monthNames: [String] = [] - @ObservationIgnored private var _monthNamesFull: [String] = [] @ObservationIgnored private var _month: Int = -1 @ObservationIgnored private var _precise_month: Int = -1 + @ObservationIgnored private var _leap_month: Int = -1 @ObservationIgnored private var _day: Int = -1 @ObservationIgnored private var _sunTimes: [Date?] = [] @ObservationIgnored private var _moonTimes: [Date?] = [] @@ -552,7 +566,7 @@ extension Array { updateHour() } - private init(compact: Bool, time: Date, calendar: Calendar, location: CGPoint?, globalMonth: Bool, apparentTime: Bool, year: Int, year_length: Double, solarTerms: [Date], evenSolarTerms: [Date], oddSolarTerms: [Date], moonEclipses: [Date], fullMoons: [Date], monthNames: [String], monthNamesFull: [String], month: Int, precise_month: Int, day: Int, sunTimes: [Date?], moonTimes: [Date?], startHour: Date, endHour: Date, hourNames: [String], hour_string: String, quarter_string: String) { + private init(compact: Bool, time: Date, calendar: Calendar, location: CGPoint?, globalMonth: Bool, apparentTime: Bool, year: Int, year_length: Double, numberOfMonths: Int, solarTerms: [Date], evenSolarTerms: [Date], oddSolarTerms: [Date], moonEclipses: [Date], fullMoons: [Date], month: Int, precise_month: Int, leap_month: Int, day: Int, sunTimes: [Date?], moonTimes: [Date?], startHour: Date, endHour: Date, hourNames: [String], hour_string: String, quarter_string: String) { self._compact = compact self._time = time self._calendar = calendar @@ -561,15 +575,15 @@ extension Array { self._apparentTime = apparentTime self._year = year self._year_length = year_length + self._numberOfMonths = numberOfMonths self._solarTerms = solarTerms self._evenSolarTerms = evenSolarTerms self._oddSolarTerms = oddSolarTerms self._moonEclipses = moonEclipses self._fullMoons = fullMoons - self._monthNames = monthNames - self._monthNamesFull = monthNamesFull self._month = month self._precise_month = precise_month + self._leap_month = leap_month self._day = day self._sunTimes = sunTimes self._moonTimes = moonTimes @@ -581,7 +595,7 @@ extension Array { } var copy: ChineseCalendar { - ChineseCalendar(compact: _compact, time: _time, calendar: _calendar, location: _location, globalMonth: _globalMonth, apparentTime: _apparentTime, year: _year, year_length: _year_length, solarTerms: _solarTerms, evenSolarTerms: _evenSolarTerms, oddSolarTerms: _oddSolarTerms, moonEclipses: _moonEclipses, fullMoons: _fullMoons, monthNames: _monthNames, monthNamesFull: _monthNamesFull, month: _month, precise_month: _precise_month, day: _day, sunTimes: _sunTimes, moonTimes: _moonTimes, startHour: _startHour, endHour: _endHour, hourNames: _hourNames, hour_string: _hour_string, quarter_string: _quarter_string) + ChineseCalendar(compact: _compact, time: _time, calendar: _calendar, location: _location, globalMonth: _globalMonth, apparentTime: _apparentTime, year: _year, year_length: _year_length, numberOfMonths: _numberOfMonths, solarTerms: _solarTerms, evenSolarTerms: _evenSolarTerms, oddSolarTerms: _oddSolarTerms, moonEclipses: _moonEclipses, fullMoons: _fullMoons, month: _month, precise_month: _precise_month, leap_month: _leap_month, day: _day, sunTimes: _sunTimes, moonTimes: _moonTimes, startHour: _startHour, endHour: _endHour, hourNames: _hourNames, hour_string: _hour_string, quarter_string: _quarter_string) } private func updateYear() { @@ -645,33 +659,18 @@ extension Array { count = 0 i += 1 } - if thisEclipse >= solar_terms[0], thisEclipse < solar_terms[24] { + if thisEclipse > solar_terms[0], thisEclipse <= solar_terms[24] { monthCount.insert(thisEclipse) } } - var months_compact = [String]() - var months = [String]() - if monthCount.count == 12 { - months_compact = (Self.month_chinese_compact + Self.month_chinese_compact).slice(to: eclipse.count) - months = (Self.month_chinese + Self.month_chinese).slice(to: eclipse.count) - } else if monthCount.count > 12 { - var leap = 0 - var leapLabel = "" + var leap_month = -1 + if monthCount.count > 12 { for i in 0.. 1 { - return "\(chinese_month)\(chinese_day)" - } else { - return "\(chinese_month)\(chinese_day)日" - } + "\(monthString)\(dayString)" } var timeString: String { @@ -890,8 +884,25 @@ extension Array { } return phases } + + var monthNames: [String] { + var names = [String]() + let all_names = _compact ? Self.month_chinese_compact : Self.month_chinese + for i in 0..<_numberOfMonths { + let name = if _leap_month >= 0 && i > _leap_month { + all_names[(i - 3) %% 12] + } else if _leap_month >= 0 && i == _leap_month { + Self.leapLabel + all_names[(i - 3) %% 12] + } else { + all_names[(i - 2) %% 12] + } + names.append(Self.alternativeMonthName[name] ?? name) + } + return names + } var monthTicks: Ticks { + let months = self.monthNames var ticks = Ticks() var monthDivides: [Double] if _globalMonth { @@ -899,28 +910,33 @@ extension Array { } else { monthDivides = _moonEclipses.map { _solarTerms[0].distance(to: _calendar.startOfDay(for: $0, apparent: apparentTime, location: location)) / _year_length } } - monthDivides = monthDivides.filter { $0 > 0 && $0 < 1 } + monthDivides = monthDivides.filter { $0 > 0 && $0 <= 1 } + guard months.count == monthDivides.count else { return ticks } var previousMonthDivide = 0.0 var monthNames = [Ticks.TickName]() let minMonthLength: Double = (_compact ? 0.009 : 0.006) for i in 0.. minMonthLength * Double(_monthNames[i].count) { + if position - previousMonthDivide > minMonthLength * Double(months[i].count) { monthNames.append(Ticks.TickName( pos: position, - name: _monthNames[i], + name: months[i], active: previousMonthDivide <= currentDayInYear )) } previousMonthDivide = monthDivides[i] } - let position = (1 + previousMonthDivide) / 2 - if position - previousMonthDivide > minMonthLength * Double(_monthNames[(monthDivides.count) %% _monthNames.count].count) { - monthNames.append(Ticks.TickName( - pos: position, - name: _monthNames[(monthDivides.count) %% _monthNames.count], - active: previousMonthDivide <= currentDayInYear - )) + if let lastDivide = monthDivides.last, lastDivide < 1 { + let position = (1 + previousMonthDivide) / 2 + if position - previousMonthDivide > minMonthLength * Double(months[0].count) { + monthNames.append(Ticks.TickName( + pos: position, + name: months[0], + active: previousMonthDivide <= currentDayInYear + )) + } + } else { + monthDivides.popLast() } ticks.majorTicks = [0] + monthDivides @@ -959,9 +975,9 @@ extension Array { let allDayNames: [String] if _compact { - allDayNames = Self.day_chinese_compact.slice(to: monthLengthInWholeDays) + [Self.day_chinese_compact[0]] + allDayNames = Self.day_chinese_compact.slice(to: numberOfDaysInMonth) + [Self.day_chinese_compact[0]] } else { - allDayNames = Self.day_chinese.slice(to: monthLengthInWholeDays) + [Self.day_chinese[0]] + allDayNames = Self.day_chinese.slice(to: numberOfDaysInMonth) + [Self.day_chinese[0]] } var dayNames = [Ticks.TickName]() @@ -1182,6 +1198,15 @@ extension Array { var preciseMonth: Int { _globalMonth ? _precise_month : _month } + + var nominalMonth: Int { + let inLeapMonth = _leap_month >= 0 && _month >= _leap_month + return ((_month + (inLeapMonth ? 0 : 1) - 3) %% 12) + 1 + } + + var isLeapMonth: Bool { + _leap_month >= 0 && _month == _leap_month + } var day: Int { _day + 1 @@ -1190,8 +1215,12 @@ extension Array { var time: Date { _time } + + var numberOfMonths: Int { + _numberOfMonths + } - var monthLengthInWholeDays: Int { + var numberOfDaysInMonth: Int { let month = _globalMonth ? _precise_month : _month let monthStartDate = _calendar.startOfDay(for: _moonEclipses[month], apparent: apparentTime, location: location) let monthEndDate = _calendar.startOfDay(for: _moonEclipses[month + 1], apparent: apparentTime, location: location) @@ -1459,17 +1488,12 @@ extension Array { } var lunarHoliday: String? { - if let holiday = Self.holidays[self.dateString] { - return holiday - } else { - let nextDay = self.copy - nextDay.update(time: self.startOfNextDay + 1) - if nextDay.dateString == "正月一日" { - return Self.holidays["大年三十"] - } else { - return nil + for holiday in Self.holidays.keys { + if self.equals(date: holiday) { + return Self.holidays[holiday] } } + return nil } var holidays: [String] { @@ -1491,4 +1515,12 @@ extension Array { } return holidays } + + func equals(date: ChineseDate) -> Bool { + if date.day > 0 { + return date.leap == isLeapMonth && date.month == nominalMonth && date.day == day + } else { + return date.leap == isLeapMonth && date.month == nominalMonth && date.day == day - numberOfDaysInMonth - 1 + } + } } diff --git a/Shared/DataModel/PlanetModel.swift b/Shared/DataModel/PlanetModel.swift index ecb262d..4cf5722 100644 --- a/Shared/DataModel/PlanetModel.swift +++ b/Shared/DataModel/PlanetModel.swift @@ -1,8 +1,8 @@ // // PlanetModel.swift -// ChineseTime +// Chinendar // -// Created by LEO Yoon-Tsaw on 10/7/21. +// Created by Leo Liu on 10/7/21. // import Foundation diff --git a/Shared/DataModel/ThemeData.swift b/Shared/DataModel/ThemeData.swift index 94dec8e..a2e7628 100644 --- a/Shared/DataModel/ThemeData.swift +++ b/Shared/DataModel/ThemeData.swift @@ -1,6 +1,6 @@ // -// Layout.swift -// Chinese Time +// ThemeData.swift +// Chinendar // // Created by Leo Liu on 7/7/23. // @@ -14,6 +14,8 @@ import SystemConfiguration import UIKit #elseif os(watchOS) import WatchKit +#elseif os(visionOS) +import VisionKit #endif typealias ThemeData = DataSchemaV2.Layout @@ -24,19 +26,21 @@ extension ThemeData: Identifiable, Hashable { } #if os(macOS) - static let groupId = Bundle.main.object(forInfoDictionaryKey: "GroupID") as! String -#elseif os(iOS) - static let groupId = "group.ChineseTime" + static let groupId = Bundle.main.object(forInfoDictionaryKey: "GroupID") as! String +#elseif os(iOS) || os(visionOS) + static let groupId = "group.ChineseTime" #elseif os(watchOS) - static let groupId = "group.ChineseTime.Watch" + static let groupId = "group.ChineseTime.Watch" #endif #if os(macOS) - static let deviceName = SCDynamicStoreCopyComputerName(nil, nil).map { String($0) } ?? "Mac" + static let deviceName = SCDynamicStoreCopyComputerName(nil, nil).map { String($0) } ?? "Mac" #elseif os(iOS) - @MainActor static let deviceName = UIDevice.current.name + @MainActor static let deviceName = UIDevice.current.name #elseif os(watchOS) - static let deviceName = WKInterfaceDevice.current().name + static let deviceName = WKInterfaceDevice.current().name +#elseif os(visionOS) + @MainActor static let deviceName = UIDevice.current.name #endif static let defaultName = NSLocalizedString("Default", comment: "Default save file name") @@ -107,7 +111,7 @@ private func createContainer(schema: Schema, migrationPlan: SchemaMigrationPlan. } enum DataSchemaV2: VersionedSchema { - static let versionIdentifier: Schema.Version = .init(1, 1, 0) + static let versionIdentifier: Schema.Version = .init(1, 1, 1) static var models: [any PersistentModel.Type] { [Layout.self] } diff --git a/Shared/Environments.swift b/Shared/Environments.swift index 470e25d..205282d 100644 --- a/Shared/Environments.swift +++ b/Shared/Environments.swift @@ -1,6 +1,6 @@ // -// File.swift -// Chinese Time +// Environments.swift +// Chinendar // // Created by Leo Liu on 8/29/23. // diff --git a/Shared/InfoPlist.xcstrings b/Shared/InfoPlist.xcstrings index d606921..4939eb9 100644 --- a/Shared/InfoPlist.xcstrings +++ b/Shared/InfoPlist.xcstrings @@ -7,7 +7,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Chinese Time" + "value" : "Chinendar" } }, "zh-Hans" : { @@ -30,7 +30,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Chinese Time" + "value" : "Chinendar" } }, "zh-Hans" : { diff --git a/Shared/Locale.swift b/Shared/Locale.swift index 88d63a2..9f84fdf 100644 --- a/Shared/Locale.swift +++ b/Shared/Locale.swift @@ -1,6 +1,6 @@ // // Locale.swift -// Chinese Time +// Chinendar // // Created by Leo Liu on 8/18/23. // diff --git a/Shared/Localizable.xcstrings b/Shared/Localizable.xcstrings index d79ccc3..77e13bb 100644 --- a/Shared/Localizable.xcstrings +++ b/Shared/Localizable.xcstrings @@ -258,19 +258,19 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "# Why create this Chinese Time?\nIs the Chinese calendar still useful in daily life? In reality, it is practically useless, but as a form of cultural heritage, it can still serve as an exquisite decoration. Having seen too many traditional but outdated Chinese calendars, I had long wanted to create a modern one. That's why I made this.\nInspired by the design of watches, the months and years are displayed in a circular format similar to hours and minutes. With this design, the year, month, day, and hour can be easily read at a glance. Moreover, the calendar can also show the 24 solar terms, lunar phases, and leap months in an intuitive way.\n# What is the Chinese calendar?\nThe Chinese calendar is a traditional lunar-solar calendar system that is based on astronomical observations. It has a simple philosophy and unique beauty, but is challenging to calculate. Fortunately, modern technology has made the calculation much easier. In the past, people often wondered why the Chinese calendar date is irregular when comparing with Gregorian calendar dates, and do not follow a predictable pattern. However, after thorough study, the rules governing the Chinese calendar were found to be simple, yet the calculations they required were incredibly complex.\nIn the lunar-solar calendar system, the lunar part relates to the moon. The new moon, which occurs when the sun and the moon are at the same ecliptic longitude (and latitude during a solar eclipse), marks the first day of a month. This is an easily observable celestial phenomenon as the moon cannot be seen on that day. The full moon, on the other hand, is not as precisely observable since it can be slightly crescent or gibbous, making it difficult to determine the exact day. Hence, the ancient Chinese used the new moon to mark the beginning of month. Therefore, the first rule of the Chinese calendar system is: **the day of the new moon marks the first day of the month, and the period between two consecutive new moons is one month**.\nThe solar part refers to the sun in the Chinese calendar system. While the months are determined by the moon, but there needs a link between the lunar months and seasons. Therefore, it is the second rule that **winter solstices are always in the eleventh lunar month**. The winter solstice is the most important solar term, and it is easier to observe than the other solar terms, except for the summer solstice. The choice of the winter solstice as the starting point of the year might also be due to the fact that people have more free time during the winter season to make astronomical observations. Therefore, the winter solstice has become a significant holiday in ancient Chinese culture.\nOnce the 11th month is determined, the months between 2 consecutive winter solstices are named in sequence. Ideally, there should be 12 months between 2 winter solstices. However, the average year is 365.24 days long, which means there are 12.37 lunar months with an average of 29.53 days each. Sometimes there can be 13 months between two winter solstices, which requires an extra month, known as the leap month, to sync the lunar aspect to solar. To determine which month to add, the Chinese chose to use the 12 Even Solar Terms as a reference. There are 11 Even Solar Terms between two winter solstices, and if there are 13 lunar months, there will be a month without an Even Solar Term, which will be designated as the leap month. If there are 2 months without a solar term, only the first one will be designated as the leap month. Hence, the third and final rule: **if there are 13 new moons between two winter solstices, the first lunar month without an Even Solar Term repeats its preceding month, and is call Leap Month.**\n# Since it's an astronomical calendar, could the calculated dates differ due to different time zones?\nYes. \nThe Chinese calendar is defined based on celestial observations, and the ancient Chinese people mostly lived in East Asia, so their observations of celestial phenomena were similar. However, with global awareness, the problem of time zones arises. For example, if a new moon is observed in Beijing at 8 a.m. on the 23rd, that day is considered the first day of the month. But in New York, the new moon at 7 p.m. on the 22nd, which means 22nd is the first day. Therefore, countries that use the same calendar system, such as China, Korea, and Vietnam, may have slightly different dates for the first day of a month due to time differences.\nWhile the difference of month starts due to time difference is limited to 1 day, for a leap month, the difference can be much greater. The average interval between two solar terms is 30.44 days, and the average length of a lunar month is 29.53 days, which is not much different. Therefore, if there is no Even Solar Term within a lunar month, that month will be closely surrounded by Even Solar Terms. If there is a difference in the first day of the month, which month contains an Even Solar Term is a major question, and this difference can lead up to four months difference. While a difference of one day can be accepted, a difference of four months cannot be accepted.\nTherefore, there is a more precise way for leap month calculation: instead of counting EST in a month, count EST between 2 new moon moments. Although EST may fall on different dates due to time zones, the relative order of new moon and EST remains undisturbed. This is the \"**Finest Precision**\" option, which is not enabled by default and can be manually turned on.\n# What are Chinese Hour and sub-hour Quarter\nWhen you read \"3 quarters past noon\", what time is it exactly? What is the relationship between Hour and Quarter?. In fact, Hour and Quarter are different from what they mean in English.\nThe 12 words describing Hours originally referred to the twelve astrological signs used for year counting (associated with Jupiter's 11.83-year period). The oldest Hour counts was neither fixed at 12 a day, but can be wither 10 or 16. The length of each was also not fixed and was related to natural phenomena or daily activities, such as dawn, dusk, and breakfast time, and bedtime. The earliest precise time measurement was the water clock, on which Quarters were marked. **A day is divided into 100 Quarters with each Quarter equivalent to 14 minutes and 24 seconds**. However, counting up to 100 is difficult, so people used the 12 Hours in combination with Quarters to create the concept of a x Quarters past y Hour. The maximum Quarters after an Hour is limited, people could easily count them. This greatly improved readability.\nHowever, there was a problem: the interval between 2 Hours is 120 minutes, while the interval between Quarters is 14 minutes and 24 seconds. The former cannot divide the latter in whole. Therefore, only 4 Hours perfectly coincide with the Quarter while the others do not. Therefore, the duration of the first Quarter after each Hour is not the same. Those after the 1st, 4th, 7th and 10th Hour are complete Quarters, while those following other Hours are incomplete. The greatest common divisor of 60 minutes and 14.4 minutes is 2 minutes and 24 seconds, which is called a Minor Quarter. There are 6 Minor Quarters in a Quarter, and they were marked on the innermost ring on the clock.\nIt is worth noting that the ancient Chinese Hour refers to a moment, not a period. For example, the 1st Hour is the moment of 0:00, not the two-hour period from 23:00 the previous day to 1:00 the next day.\nAs for why Hour gradually had evolved to become time period, it is because with the advancement of timekeeping, an exquisite clock that displayed the Hour appeared some time in Song Dynasty. At noon, the 7th Hour appears in the center of the clock window, but the Hour did not just appear out of nowhere, instead, starting from 11:00, the 7th Hour sign enters in the corner of the window, at 12:00 reaches the center, and at 13:00 it disappears from view. This whole time period was then named after the Hour. In addition, each Hour was divided into two hours, with the first hour prefixed by Initial and the second hour by Proper.\n# Apparent Solar Time and Standard Time\nToday's timekeeping uses time zones. For example, when using UTC+8, noon is the noon at the meridian of 120°E longitude, and for places not exactly at 120°E, the true noon time is not 12:00. There is a **longitude time difference**. In addition, because the earth's orbit around the sun is not circular, it moves faster near perihelion and slower near aphelion, causing a slightly longer or shorter day than the average; this also affects the time of noon, which is called the **equation of time**.\nStandard time is the time commonly used in daily life, while apparent solar time is the time corrected for these two differences. The apparent solar noon is when the sun reaches its highest in the day, and the apparent solar midnight is when the sun is directly opposite behind the Earth. However, the noon and midnight in standard time have no astronomical significance.\n# What are the color marks on the Year Ring?\nIn the traditional Chinese practice, in addition to calculating days and time, the positions of the **five planets (Mercury, Venus, Mars, Jupiter and Saturn)** were also essential, for they are bright and moving. With modern astronomy, the positions of planets and moons can be calculated accurately. In particular, the positions of Jupiter and Saturn were also used for year counting in ancient China. Jupiter orbits the sun once every 11.86 years, which is approximately 12 years, so Jupiter's chronological year evolved into the Earthly Branches system of years. Saturn orbits the sun once every 29.5 years, and when combined with Jupiter, they form a cycle of 60 years, which is the famous Heavenly Stems and Earthly Branches system of years that is still used today.\nThere are **6 color marks on the Year Ring (5 planets and moon)**. To understand their position, first comes the fact that the 24 solar terms are both dates and ecliptic positions. For example, Spring Equinox is the position of the Sun on the ecliptic plane on the Spring Equinox day. If for example, Jupiter is at Spring Equinox means it's at the same direction as the Sun was on that day. The positions of Mercury and Venus are always near the Sun because their orbits are within the Earth's orbit. However, the positions of Mars, Jupiter and Saturn may not be near the Sun. The planets that are in front of the Sun (the transparent part on the Year Ring) rise before sunrise and set before sunset, while the planets that are behind the sun (the solid color part) rise after sunrise and set after sunset.\n# What are the color marks on the Month Ring?\nThere are generally 4 types: **New Moon, Full Moon, Odd Solar Term** and **Even Solar Term**, of which the exact colors can be changed in the settings. If the leap month is configured to \"Finest Precision\", then the Month Ring starts from the moment of the New Moon, and thus invalidates the need for New Moon mark, leaving only the other three color marks. The Full Moon marks the fullest moon moment in a month, you can observe for several months and tell whether the moon is the fullest on the 15th, 16th, or 17th. The Solar Term marks also correspond to the 24 solar terms on the Year Ring.\nWhen it is close to the time of a New Moon, Full Moon or Solar Term, the same color mark will also appear on the Day Ring and Hour Ring for accuracy. These four color marks appear on the **outer edge** of the Day and Hour rings.\n# Times of sunrise and moonrise\nThe times of sunrise and moonrise were crucial to ancient people and were essential in agriculture. So they are indispensable in the Chinese calendar.\nIf the location is enabled in settings, the local times of sunrise and moonrise will be displayed on the **inner edge** of Day Ring. When it is close to such a time, the same color mark will also appear on Hour Ring, also on the inner edge. There are 7 color marks in this category: **Sunrise, Noon, Sunset, Midnight, Moonrise, Moon at Meridian** and **Moonset**. The specific colors can also be changed in the settings. If the solar time setting is set to Apparent S Time, noon and midnight are no longer marked with color marks, since they are already apparent by time itself.\n# Terminologies\n**Solar Terms** are positions of Earth on its orbit. 4 solar terms are famous: 冬至 (Winter Solstice), 春分 (Spring Equinox), 夏至 (Summer Solstice), 秋分 (Autumn Equinox). Between each 2 of them, the 90° areas are further divided into 6 smaller divisions, with 5 more solar terms in each quadrant. This makes the total number of Solar Terms to 24, which are apart by 15° in the ecliptic plane.\n**Odd Solar Terms** are odd ones in solar terms, and thus are apart from each other by 30°. They do not include any of the equinoxes or solstices. The 12 of them are: 小寒, 立春, 驚蟄/啓蟄, 清明, 立夏, 芒種, 小暑, 立秋, 白露, 寒露, 立冬 and 大雪. 驚蟄/啓蟄 and 清明 were once Even Solar Terms before 85AC, then switched with 雨水 and 穀雨 respectively.\n**Even Solar Terms** are even ones in solar terms, also apart by 30°. They help determine the Leap Month in Chinese calendar (refer to \"What is the Chinese calendar\" section for details). The 12 of them are: 冬至 (Winter Solstice), 大寒, 雨水, 春分 (Spring Equinox), 穀雨, 小滿, 夏至 (Summer Solstice), 大暑, 處暑, 秋分 (Autumn Equinox), 霜降 and 小雪.\n**New Moon** is the moment when the sun and the moon are at the same ecliptic longitude (and latitude during a solar eclipse).\n**Full Moon** is the moment when the sun and the moon are opposite to each other over the Earth (lunar eclipse can happen on this moment).\n**Hour names** are 12 Earthly Branches in Chinese, apart from each other by 2 hours measured today. from 12am to before 12pm are: 子, 丑, 寅, 卯, 辰 and 巳, then from 12pm to before 12am next day are: 午, 未, 申, 酉, 戌 and 亥." + "value" : "# Why create this Chinendar?\nChinendar is abbreviation of **Chinese Calendar**. Is the Chinese calendar still useful in daily life? In reality, it is practically useless, but as a form of cultural heritage, it can still serve as an exquisite decoration. Having seen too many traditional but outdated Chinese calendars, I had long wanted to create a modern one. That's why I made this.\nInspired by the design of watches, the months and years are displayed in a circular format similar to hours and minutes. With this design, the year, month, day, and hour can be easily read at a glance. Moreover, the calendar can also show the 24 solar terms, lunar phases, and leap months in an intuitive way.\n# What is the Chinese calendar?\nThe Chinese calendar is a traditional **lunar-solar calendar system** that is based on astronomical observations. It has a simple philosophy and unique beauty, but is challenging to calculate. Fortunately, modern technology has made the calculation much easier. In the past, people often wondered why the Chinese calendar date is irregular when comparing with Gregorian calendar dates, and do not follow a predictable pattern. However, after thorough study, the rules governing the Chinese calendar were found to be simple, yet the calculations they required were incredibly complex.\nIn the lunar-solar calendar system, the lunar part relates to the moon. The new moon, which occurs when the sun and the moon are at the same ecliptic longitude (and latitude during a solar eclipse), marks the first day of a month. This is an easily observable celestial phenomenon as the moon cannot be seen on that day. The full moon, on the other hand, is not as precisely observable since it can be slightly crescent or gibbous, making it difficult to determine the exact day. Hence, the ancient Chinese used the new moon to mark the beginning of month. Therefore, the first rule of the Chinese calendar system is: **the day of the new moon marks the first day of the month, and the period between two consecutive new moons is one month**.\nThe solar part refers to the sun in the Chinese calendar system. While the months are determined by the moon, but there needs a link between the lunar months and seasons. Therefore, it is the second rule that **winter solstices are always in the eleventh lunar month**. The winter solstice is the most important solar term, and it is easier to observe than the other solar terms, except for the summer solstice. The choice of the winter solstice as the starting point of the year might also be due to the fact that people have more free time during the winter season to make astronomical observations. Therefore, the winter solstice has become a significant holiday in ancient Chinese culture.\nOnce the 11th month is determined, the months between 2 consecutive winter solstices are named in sequence. Ideally, there should be 12 months between 2 winter solstices. However, the average year is 365.24 days long, which means there are 12.37 lunar months with an average of 29.53 days each. Sometimes there can be 13 months between two winter solstices, which requires an extra month, known as the leap month, to sync the lunar aspect to solar. To determine which month to add, the Chinese chose to use the 12 Even Solar Terms as a reference. There are 11 Even Solar Terms between two winter solstices, and if there are 13 lunar months, there will be a month without an Even Solar Term, which will be designated as the leap month. If there are 2 months without a solar term, only the first one will be designated as the leap month. Hence, the third and final rule: **if there are 13 new moons between two winter solstices, the first lunar month without an Even Solar Term repeats its preceding month, and is call Leap Month.**\n# Since it's an astronomical calendar, could the calculated dates differ due to different time zones?\nYes. \nThe Chinese calendar is defined based on celestial observations, and the ancient Chinese people mostly lived in East Asia, so their observations of celestial phenomena were similar. However, with global awareness, the problem of time zones arises. For example, if a new moon is observed in Beijing at 8 a.m. on the 23rd, that day is considered the first day of the month. But in New York, the new moon at 7 p.m. on the 22nd, which means 22nd is the first day. Therefore, countries that use the same calendar system, such as China, Korea, and Vietnam, may have slightly different dates for the first day of a month due to time differences.\nWhile the difference of month starts due to time difference is limited to 1 day, for a leap month, the difference can be much greater. The average interval between two solar terms is 30.44 days, and the average length of a lunar month is 29.53 days, which is not much different. Therefore, if there is no Even Solar Term within a lunar month, that month will be closely surrounded by Even Solar Terms. If there is a difference in the first day of the month, which month contains an Even Solar Term is a major question, and this difference can lead up to four months difference. While a difference of one day can be accepted, a difference of four months cannot be accepted.\nTherefore, there is a more precise way for leap month calculation: instead of counting EST in a month, count EST between 2 new moon moments. Although EST may fall on different dates due to time zones, the relative order of new moon and EST remains undisturbed. This is the \"**Finest Precision**\" option, which is not enabled by default and can be manually turned on.\n# What are Chinese Hour and sub-hour Quarter\nWhen you read \"3 quarters past noon\", what time is it exactly? What is the relationship between Hour and Quarter?. In fact, Hour and Quarter are different from what they mean in English.\nThe 12 words describing Hours originally referred to the twelve astrological signs used for year counting (associated with Jupiter's 11.83-year period). The oldest Hour counts was neither fixed at 12 a day, but can be wither 10 or 16. The length of each was also not fixed and was related to natural phenomena or daily activities, such as dawn, dusk, and breakfast time, and bedtime. The earliest precise time measurement was the water clock, on which Quarters were marked. **A day is divided into 100 Quarters with each Quarter equivalent to 14 minutes and 24 seconds**. However, counting up to 100 is difficult, so people used the 12 Hours in combination with Quarters to create the concept of a x Quarters past y Hour. The maximum Quarters after an Hour is limited, people could easily count them. This greatly improved readability.\nHowever, there was a problem: the interval between 2 Hours is 120 minutes, while the interval between Quarters is 14 minutes and 24 seconds. The former cannot divide the latter in whole. Therefore, only 4 Hours perfectly coincide with the Quarter while the others do not. Therefore, the duration of the first Quarter after each Hour is not the same. Those after the 1st, 4th, 7th and 10th Hour are complete Quarters, while those following other Hours are incomplete. The greatest common divisor of 60 minutes and 14.4 minutes is 2 minutes and 24 seconds, which is called a Minor Quarter. There are 6 Minor Quarters in a Quarter, and they were marked on the innermost ring on the clock.\nIt is worth noting that the ancient Chinese Hour refers to a moment, not a period. For example, the 1st Hour is the moment of 0:00, not the two-hour period from 23:00 the previous day to 1:00 the next day.\nAs for why Hour gradually had evolved to become time period, it is because with the advancement of timekeeping, an exquisite clock that displayed the Hour appeared some time in Song Dynasty. At noon, the 7th Hour appears in the center of the clock window, but the Hour did not just appear out of nowhere, instead, starting from 11:00, the 7th Hour sign enters in the corner of the window, at 12:00 reaches the center, and at 13:00 it disappears from view. This whole time period was then named after the Hour. In addition, each Hour was divided into two hours, with the first hour prefixed by Initial and the second hour by Proper.\n# Apparent Solar Time and Standard Time\nToday's timekeeping uses time zones. For example, when using UTC+8, noon is the noon at the meridian of 120°E longitude, and for places not exactly at 120°E, the true noon time is not 12:00. There is a **longitude time difference**. In addition, because the earth's orbit around the sun is not circular, it moves faster near perihelion and slower near aphelion, causing a slightly longer or shorter day than the average; this also affects the time of noon, which is called the **equation of time**.\nStandard time is the time commonly used in daily life, while apparent solar time is the time corrected for these two differences. The apparent solar noon is when the sun reaches its highest in the day, and the apparent solar midnight is when the sun is directly opposite behind the Earth. However, the noon and midnight in standard time have no astronomical significance.\n# What are the color marks on the Year Ring?\nIn the traditional Chinese practice, in addition to calculating days and time, the positions of the **five planets (Mercury, Venus, Mars, Jupiter and Saturn)** were also essential, for they are bright and moving. With modern astronomy, the positions of planets and moons can be calculated accurately. In particular, the positions of Jupiter and Saturn were also used for year counting in ancient China. Jupiter orbits the sun once every 11.86 years, which is approximately 12 years, so Jupiter's chronological year evolved into the Earthly Branches system of years. Saturn orbits the sun once every 29.5 years, and when combined with Jupiter, they form a cycle of 60 years, which is the famous Heavenly Stems and Earthly Branches system of years that is still used today.\nThere are **6 color marks on the Year Ring (5 planets and moon)**. To understand their position, first comes the fact that the 24 solar terms are both dates and ecliptic positions. For example, Spring Equinox is the position of the Sun on the ecliptic plane on the Spring Equinox day. If for example, Jupiter is at Spring Equinox means it's at the same direction as the Sun was on that day. The positions of Mercury and Venus are always near the Sun because their orbits are within the Earth's orbit. However, the positions of Mars, Jupiter and Saturn may not be near the Sun. The planets that are in front of the Sun (the transparent part on the Year Ring) rise before sunrise and set before sunset, while the planets that are behind the sun (the solid color part) rise after sunrise and set after sunset.\n# What are the color marks on the Month Ring?\nThere are generally 4 types: **New Moon, Full Moon, Odd Solar Term** and **Even Solar Term**, of which the exact colors can be changed in the settings. If the leap month is configured to \"Finest Precision\", then the Month Ring starts from the moment of the New Moon, and thus invalidates the need for New Moon mark, leaving only the other three color marks. The Full Moon marks the fullest moon moment in a month, you can observe for several months and tell whether the moon is the fullest on the 15th, 16th, or 17th. The Solar Term marks also correspond to the 24 solar terms on the Year Ring.\nWhen it is close to the time of a New Moon, Full Moon or Solar Term, the same color mark will also appear on the Day Ring and Hour Ring for accuracy. These four color marks appear on the **outer edge** of the Day and Hour rings.\n# Times of sunrise and moonrise\nThe times of sunrise and moonrise were crucial to ancient people and were essential in agriculture. So they are indispensable in the Chinese calendar.\nIf the location is enabled in settings, the local times of sunrise and moonrise will be displayed on the **inner edge** of Day Ring. When it is close to such a time, the same color mark will also appear on Hour Ring, also on the inner edge. There are 7 color marks in this category: **Sunrise, Noon, Sunset, Midnight, Moonrise, Moon at Meridian** and **Moonset**. The specific colors can also be changed in the settings. If the solar time setting is set to Apparent S Time, noon and midnight are no longer marked with color marks, since they are already apparent by time itself.\n# Terminologies\n**Solar Terms** are positions of Earth on its orbit. 4 solar terms are famous: 冬至 (Winter Solstice), 春分 (Spring Equinox), 夏至 (Summer Solstice), 秋分 (Autumn Equinox). Between each 2 of them, the 90° areas are further divided into 6 smaller divisions, with 5 more solar terms in each quadrant. This makes the total number of Solar Terms to 24, which are apart by 15° in the ecliptic plane.\n**Odd Solar Terms** are odd ones in solar terms, and thus are apart from each other by 30°. They do not include any of the equinoxes or solstices. The 12 of them are: 小寒, 立春, 驚蟄/啓蟄, 清明, 立夏, 芒種, 小暑, 立秋, 白露, 寒露, 立冬 and 大雪. 驚蟄/啓蟄 and 清明 were once Even Solar Terms before 85AC, then switched with 雨水 and 穀雨 respectively.\n**Even Solar Terms** are even ones in solar terms, also apart by 30°. They help determine the Leap Month in Chinese calendar (refer to \"What is the Chinese calendar\" section for details). The 12 of them are: 冬至 (Winter Solstice), 大寒, 雨水, 春分 (Spring Equinox), 穀雨, 小滿, 夏至 (Summer Solstice), 大暑, 處暑, 秋分 (Autumn Equinox), 霜降 and 小雪.\n**New Moon** is the moment when the sun and the moon are at the same ecliptic longitude (and latitude during a solar eclipse).\n**Full Moon** is the moment when the sun and the moon are opposite to each other over the Earth (lunar eclipse can happen on this moment).\n**Hour names** are 12 Earthly Branches in Chinese, apart from each other by 2 hours measured today. from 12am to before 12pm are: 子, 丑, 寅, 卯, 辰 and 巳, then from 12pm to before 12am next day are: 午, 未, 申, 酉, 戌 and 亥." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "# 何以要做此华历?\n华历在生活中还有用吗?其实基本无用了,但作为一种文化传承,依然可以作为精美的装点。见惯了千篇一律,透露出十几年前那既不传统,又不现代的万年历,我就一直想做一款现代的华历,于是就做了。\n灵感取自表,把年月也仿似日、时一般做成轮。如此,年月日时皆一目了然,一年中的廿四节气、大小月、闰月亦能聚在一盘,直观呈现。\n# 华历是什么?\n华历是阴阳历,亦系天文历,一切皆以天文定,理念朴素,自有美感;但亦因此而难于计算,所幸在现代技术面前,计算不再是问题。以前每每感念新年年年不同,日历下方的小字上的华历日期也无甚规律,可当深入研究后才发现它的规则是如此简单,而所引出的计算又是无比复杂。\n阴阳历中的“阴”指的就系月了,新月,即月与日经度重叠的那一刻(如纬度亦重叠则为日蚀),所在之日为初一。因为日与月同经,同升共落,那一日看不见月,这是非常易于观测的天象,而月圆则没那么精确,圆一点、缺一点,幅度不大时并不明显,古人以新月定初一是很朴素的。所以第一条规则即系:**新月所在之日为初一,初一至下一初一前一日为一月**。\n阴阳历中的“阳”指的系日,月是以月定的,但没有定这个月是几月。给月定名是靠太阳完成的。为了让同样的月总是处在类似的季节,便有了第二条规定:**冬至必定在冬月(十一月)**。冬至是廿四节气中最重要的,其它节气对应的月可以有前后出入,之所以是冬至,因为冬至是北半球正午日影最长的一日,比其它除了夏至外的节气都更易观测,而选冬至不选夏至可能是因为冬季比较闲,无所事事的人就把冬至过成了一个重大节日,就显得比夏至重要了。\n冬月定了,到下个冬月之间的月就按顺序取名,如果中间正好有11个整月,那么完美。但一年365.24日中平均有12.37个平均为29.53日的月,有时候两个冬月之间有12个整G月,多了一个月,就得置闰月调整,这就麻烦了。想了一想,选了廿四节气中的十二个叫中气,两个冬至之间必定有且只有11个中气,如果有12个月,必定至少有一个月是无中气的,这倒霉月就叫闰月。如果碰巧有的月占了两个中气,就可能有两个无中气月,不能都闰了。总结就是:**两个冬至之间若有13个朔,则首个无中气月为闰月**。\n# 既然是天文历,会不会时区不同,计算出的日期也不同呢\n会的。\n华历的定义是从天象来的,古人都在东亚,走不出多远,天象都差不多;而现在视野开阔了,有了全球的概念,问题就复杂了。譬如长安23日早8点朔,23日即初一;而在纽约是22日晚7点朔,22日为初一。因此同样使用这套历法的中国、韩国、越南历,因为时差的缘故,可能初一的日期就略有不同。\n初一有前后一日的出入,闰月的出入就更大了。中气的平均间隔是30.44日,月平均长29.53日,相差不大,无中气月的前后必定紧邻前后两个中气。初一稍有不同,则哪个月无中气就会有巨大差别,前后可以相差4个月。一日的出入可以接受,四个月的出入就难以接受了。\n所以这里提供了另一种置闰法:中气包含的计算规则,由初一至下个初一之间,改为朔(精确时刻)至下个朔之间。随时区不同,中气可能落在不同日期,但朔时刻与中气时刻之间的先后关系是不随时区而变化的。此即“**精确至时刻**”选项,默认不开启,需手动打开。\n# 时、刻又是什么\n午时三刻是众所难忘的台词,午时三刻究竟系几点?时与刻是何关系,想必是常见的疑问。其实时与刻是两种全然不同的计时方法。\n十二辰本是天上十二个星域,用以记年的,最古老的时辰并不固定是十二个,有十个者,也有十六个者,时长亦不定,而以自然现象或作息命名,如旦、昏、朝食、人定……。最早的精确时计是漏,漏上画好刻。**一天分为百刻,一刻合今14分24秒**。但百刻能把人眼看花,古人把十二辰用以计时,同时结合了百刻,出现了某时某刻的说法。即在百刻之上同时画上十二个时辰,在过了某个时辰后就只数该时辰后多少刻。如此大大减轻了眼睛的负担,再也不会数错了。\n但问题来了,时辰之间间隔120分钟,刻之间间隔14分24秒,不能整除。所以子、卯、午、酉四时辰与刻完美重合,其它时辰则不重合。而且时辰之后第一刻所代表的时长并不相同。子、卯、午、酉后第一刻是完整的一刻,其它时辰后第一刻则不完整。一小时60分钟与一刻14分24秒的最大公约数为2分24秒,是为一小刻,一刻内有6小刻,在最内轮中画出了小刻。\n有一点需要明确的是,古代的时辰是一个时刻,非时段,子时就是0:00那一时刻,而并非前一日23:00至后一日1:00那两小时。子时三刻指子时后又过了三刻,而不是子时中的第三刻。\n至于为何时辰会有一段时间,如子时为23:00-1:00的印象?简单来说就是随着时计进步,出现了一种显示时辰牌的时钟,12:00时“午时”出现在窗口正中,而时辰牌不可能突然凭空出现,所以从11:00开始,“午时”牌出现在窗口角落,12:00在正中,13:00离开视线,这一段时间被叫做午时。在这之后,一个时辰被分成两小时,前一小时为某时“初”,后一小时为某时“正”。\n# 真太阳时和标准时\n当今计时使用时区,譬如使用东八区时,正午就是东经120°处之正午,凡不在东经120°之处者,真正的正午时间并不是标准时的12时,此间有**经度时差**。此外,因地球绕日所行非圆,在近日点附近绕行更速,此时一日略长于平均;而于远日点附近绕行更徐,一日略饾于平均,这也会影响正午时刻,这个差值叫**真平时差**。\n标准时即日常所用之时,而真太阳时则系校正此二项差值后之时。真太阳时的午正即当日太阳行经最高点之时,子正即太阳处于地球背后正对面之时。标准时的午正、子正则并无特别天文含义。\n# 年轮上的色块是何物\n在华历中,除了计日、计时,**五行星位置(辰、太白、荧惑、填、岁)**也是必备内容。当今有了现代天文学,行星和日月行迹都能精确计算了。其中填星和岁星位置曾在古代用于计年,如岁在大荒落、岁在辰,岁绕行太阳一周11.86年,约为12年,故岁星纪年演变为地支纪年,填星绕行一周29.5年,填与岁合并,约60年一周期,由此诞生演用至今的干支纪年。\n年轮上有**6个色块(5星+月)**。24节气既是日期,也是天球上的位置,如“清明”即清明时刻太阳所处的黄道位置,而岁星在清明即岁星在同一个黄道位置。辰星与太白星因处于地球轨道内,因此它们总是在太阳(即日轮进度条末位)附近,荧惑、填、岁则未必。而位于太阳前(即年轮上虚色部分)的星会在日出前升空,日入前入地;位于太阳后(即实色部份)的星会在日出后升空,日入后入地。\n# 月轮上的色块是何物\n一般有4种:**朔、望、节、气**,具体颜色可于设置内调整。若选精确至时刻置闰法,则月轮始自朔之刻,此时不标朔,只余另三种色块。望刻所在月最圆,可多观察几个月,看月圆究竟是十五还是十六,抑或是十七?气与闰月息息相关,无气之月一般是闰月。节气色块可与年轮上所标24节气相对应。\n当接近朔、望、节、气时刻时,同样的色块也会出现在日轮和时轮上,以便更精准定时。这四种色块出现于日、时轮时位于轮**外侧**。\n# 日月出入时刻\n日出入时刻对古人来说非常重要,与廿四节气同属对农事最重要的部份,系华历中不可或缺的。\n如果打开了定位,则可计算当地日、月出入时刻,显示于日轮**内侧**,当接近某一时刻时,相同色块亦会出现于时轮上,同样位于内侧。此类色块共有7种:**日出、日中、日入、夜半、月出、月中、月入**,具体颜色亦可经设置调整。若开启了真太阳时,因午正即日中、子正即半夜,此二类不再标出。\n# 名词简介\n**节**其实并无特别名称,但为与中气相区别,此处指节气中的奇数。含:小寒、立春、惊蛰(启蛰)、清明、立夏、芒种、小暑、立秋、白露、寒露、立冬、大雪,共12个。惊蛰(启蛰)、清明二节在后汉之前为中气,后汉元和二年分别与雨水、谷雨对调。\n**中气**指节气中的偶数。含:冬至、大寒、雨水、春分、谷雨、小满、夏至、大暑、处暑、秋分、霜降、小雪,共12个。气是华历中重要的部份,无中气月与闰月有关(详见“华历是什么”条)。\n**朔**指月与日共处同一天球经度之时刻,因此月与日同出同入,月华为日光所蔽,故无月。日食发生于此刻。\n**望**指月运行至日之正对面(地球位于二者之间),日入时月出,日出时月入,故整夜可见月。月食发生于此刻。\n**五星**之古名与今不同。辰星即今之水星,又单名“星”;太白星即今之金星,以其为天空最亮之星,故名太白;荧惑即今之火星;岁星即今之木星,太岁乃与岁星相关但不同的概念;填星又作镇星,即今之土星。" + "value" : "# 何以要做此华历?\n华历即**中华日历**。华历在生活中还有用吗?其实基本无用了,但作为一种文化传承,依然可以作为精美的装点。见惯了千篇一律,透露出十几年前那既不传统,又不现代的万年历,我就一直想做一款现代的华历,于是就做了。\n灵感取自表,把年月也仿似日、时一般做成轮。如此,年月日时皆一目了然,一年中的廿四节气、大小月、闰月亦能聚在一盘,直观呈现。\n# 华历是什么?\n华历是**阴阳历**,亦系天文历,一切皆以天文定,理念朴素,自有美感;但亦因此而难于计算,所幸在现代技术面前,计算不再是问题。以前每每感念新年年年不同,日历下方的小字上的华历日期也无甚规律,可当深入研究后才发现它的规则是如此简单,而所引出的计算又是无比复杂。\n阴阳历中的“阴”指的就系月了,新月,即月与日经度重叠的那一刻(如纬度亦重叠则为日蚀),所在之日为初一。因为日与月同经,同升共落,那一日看不见月,这是非常易于观测的天象,而月圆则没那么精确,圆一点、缺一点,幅度不大时并不明显,古人以新月定初一是很朴素的。所以第一条规则即系:**新月所在之日为初一,初一至下一初一前一日为一月**。\n阴阳历中的“阳”指的系日,月是以月定的,但没有定这个月是几月。给月定名是靠太阳完成的。为了让同样的月总是处在类似的季节,便有了第二条规定:**冬至必定在冬月(十一月)**。冬至是廿四节气中最重要的,其它节气对应的月可以有前后出入,之所以是冬至,因为冬至是北半球正午日影最长的一日,比其它除了夏至外的节气都更易观测,而选冬至不选夏至可能是因为冬季比较闲,无所事事的人就把冬至过成了一个重大节日,就显得比夏至重要了。\n冬月定了,到下个冬月之间的月就按顺序取名,如果中间正好有11个整月,那么完美。但一年365.24日中平均有12.37个平均为29.53日的月,有时候两个冬月之间有12个整G月,多了一个月,就得置闰月调整,这就麻烦了。想了一想,选了廿四节气中的十二个叫中气,两个冬至之间必定有且只有11个中气,如果有12个月,必定至少有一个月是无中气的,这倒霉月就叫闰月。如果碰巧有的月占了两个中气,就可能有两个无中气月,不能都闰了。总结就是:**两个冬至之间若有13个朔,则首个无中气月为闰月**。\n# 既然是天文历,会不会时区不同,计算出的日期也不同呢\n会的。\n华历的定义是从天象来的,古人都在东亚,走不出多远,天象都差不多;而现在视野开阔了,有了全球的概念,问题就复杂了。譬如长安23日早8点朔,23日即初一;而在纽约是22日晚7点朔,22日为初一。因此同样使用这套历法的中国、韩国、越南历,因为时差的缘故,可能初一的日期就略有不同。\n初一有前后一日的出入,闰月的出入就更大了。中气的平均间隔是30.44日,月平均长29.53日,相差不大,无中气月的前后必定紧邻前后两个中气。初一稍有不同,则哪个月无中气就会有巨大差别,前后可以相差4个月。一日的出入可以接受,四个月的出入就难以接受了。\n所以这里提供了另一种置闰法:中气包含的计算规则,由初一至下个初一之间,改为朔(精确时刻)至下个朔之间。随时区不同,中气可能落在不同日期,但朔时刻与中气时刻之间的先后关系是不随时区而变化的。此即“**精确至时刻**”选项,默认不开启,需手动打开。\n# 时、刻又是什么\n午时三刻是众所难忘的台词,午时三刻究竟系几点?时与刻是何关系,想必是常见的疑问。其实时与刻是两种全然不同的计时方法。\n十二辰本是天上十二个星域,用以记年的,最古老的时辰并不固定是十二个,有十个者,也有十六个者,时长亦不定,而以自然现象或作息命名,如旦、昏、朝食、人定……。最早的精确时计是漏,漏上画好刻。**一天分为百刻,一刻合今14分24秒**。但百刻能把人眼看花,古人把十二辰用以计时,同时结合了百刻,出现了某时某刻的说法。即在百刻之上同时画上十二个时辰,在过了某个时辰后就只数该时辰后多少刻。如此大大减轻了眼睛的负担,再也不会数错了。\n但问题来了,时辰之间间隔120分钟,刻之间间隔14分24秒,不能整除。所以子、卯、午、酉四时辰与刻完美重合,其它时辰则不重合。而且时辰之后第一刻所代表的时长并不相同。子、卯、午、酉后第一刻是完整的一刻,其它时辰后第一刻则不完整。一小时60分钟与一刻14分24秒的最大公约数为2分24秒,是为一小刻,一刻内有6小刻,在最内轮中画出了小刻。\n有一点需要明确的是,古代的时辰是一个时刻,非时段,子时就是0:00那一时刻,而并非前一日23:00至后一日1:00那两小时。子时三刻指子时后又过了三刻,而不是子时中的第三刻。\n至于为何时辰会有一段时间,如子时为23:00-1:00的印象?简单来说就是随着时计进步,出现了一种显示时辰牌的时钟,12:00时“午时”出现在窗口正中,而时辰牌不可能突然凭空出现,所以从11:00开始,“午时”牌出现在窗口角落,12:00在正中,13:00离开视线,这一段时间被叫做午时。在这之后,一个时辰被分成两小时,前一小时为某时“初”,后一小时为某时“正”。\n# 真太阳时和标准时\n当今计时使用时区,譬如使用东八区时,正午就是东经120°处之正午,凡不在东经120°之处者,真正的正午时间并不是标准时的12时,此间有**经度时差**。此外,因地球绕日所行非圆,在近日点附近绕行更速,此时一日略长于平均;而于远日点附近绕行更徐,一日略饾于平均,这也会影响正午时刻,这个差值叫**真平时差**。\n标准时即日常所用之时,而真太阳时则系校正此二项差值后之时。真太阳时的午正即当日太阳行经最高点之时,子正即太阳处于地球背后正对面之时。标准时的午正、子正则并无特别天文含义。\n# 年轮上的色块是何物\n在华历中,除了计日、计时,**五行星位置(辰、太白、荧惑、填、岁)**也是必备内容。当今有了现代天文学,行星和日月行迹都能精确计算了。其中填星和岁星位置曾在古代用于计年,如岁在大荒落、岁在辰,岁绕行太阳一周11.86年,约为12年,故岁星纪年演变为地支纪年,填星绕行一周29.5年,填与岁合并,约60年一周期,由此诞生演用至今的干支纪年。\n年轮上有**6个色块(5星+月)**。24节气既是日期,也是天球上的位置,如“清明”即清明时刻太阳所处的黄道位置,而岁星在清明即岁星在同一个黄道位置。辰星与太白星因处于地球轨道内,因此它们总是在太阳(即日轮进度条末位)附近,荧惑、填、岁则未必。而位于太阳前(即年轮上虚色部分)的星会在日出前升空,日入前入地;位于太阳后(即实色部份)的星会在日出后升空,日入后入地。\n# 月轮上的色块是何物\n一般有4种:**朔、望、节、气**,具体颜色可于设置内调整。若选精确至时刻置闰法,则月轮始自朔之刻,此时不标朔,只余另三种色块。望刻所在月最圆,可多观察几个月,看月圆究竟是十五还是十六,抑或是十七?气与闰月息息相关,无气之月一般是闰月。节气色块可与年轮上所标24节气相对应。\n当接近朔、望、节、气时刻时,同样的色块也会出现在日轮和时轮上,以便更精准定时。这四种色块出现于日、时轮时位于轮**外侧**。\n# 日月出入时刻\n日出入时刻对古人来说非常重要,与廿四节气同属对农事最重要的部份,系华历中不可或缺的。\n如果打开了定位,则可计算当地日、月出入时刻,显示于日轮**内侧**,当接近某一时刻时,相同色块亦会出现于时轮上,同样位于内侧。此类色块共有7种:**日出、日中、日入、夜半、月出、月中、月入**,具体颜色亦可经设置调整。若开启了真太阳时,因午正即日中、子正即半夜,此二类不再标出。\n# 名词简介\n**节**其实并无特别名称,但为与中气相区别,此处指节气中的奇数。含:小寒、立春、惊蛰(启蛰)、清明、立夏、芒种、小暑、立秋、白露、寒露、立冬、大雪,共12个。惊蛰(启蛰)、清明二节在后汉之前为中气,后汉元和二年分别与雨水、谷雨对调。\n**中气**指节气中的偶数。含:冬至、大寒、雨水、春分、谷雨、小满、夏至、大暑、处暑、秋分、霜降、小雪,共12个。气是华历中重要的部份,无中气月与闰月有关(详见“华历是什么”条)。\n**朔**指月与日共处同一天球经度之时刻,因此月与日同出同入,月华为日光所蔽,故无月。日食发生于此刻。\n**望**指月运行至日之正对面(地球位于二者之间),日入时月出,日出时月入,故整夜可见月。月食发生于此刻。\n**五星**之古名与今不同。辰星即今之水星,又单名“星”;太白星即今之金星,以其为天空最亮之星,故名太白;荧惑即今之火星;岁星即今之木星,太岁乃与岁星相关但不同的概念;填星又作镇星,即今之土星。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "# 何以要做此華曆?\n華曆在生活中還有用嗎?其實基本無用了,但作爲一種文化傳承,依然可以作爲精美的裝點。見慣了千篇一律,透露出十幾年前那既不傳統,又不現代的萬年曆,我就一直想做一款現代的華曆,於是就做了。\n靈感取自錶,把年月也倣似日、時一般做成輪。如此,年月日時皆一目瞭然,一年中的廿四節氣、大小月、閏月亦能聚在一盤,直觀呈現。\n# 華曆是甚麼?\n華曆是陰陽曆,亦係天文曆,一切皆以天文定,理念樸素,自有美感;但亦因此而難於計算,所幸在現代技術面前,計算不再是問題。以前每每感念新年年年不同,日曆下方的小字上的華曆日期也無甚規律,可當深入研究後才發現它的規則是如此簡單,而所引出的計算又是無比複雜。\n陰陽曆中的「陰」指的就係月了,新月,即月與日經度重疊的那一刻(如緯度亦重疊則爲日蝕),所在之日爲初一。因爲日與月同經,同升共落,那一日看不見月,這是非常易於觀測的天象,而月圓則沒那麼精確,圓一點、缺一點,幅度不大時並不明顯,古人以新月定初一是很樸素的。所以第一條規則即係:**新月所在之日爲初一,初一至下一初一前一日爲一月**。\n陰陽曆中的「陽」指的係日,月是以月定的,但沒有定這個月是幾月。給月定名是靠太陽完成的。爲了讓同樣的月總是處在類似的季節,便有了第二條規定:**冬至必定在冬月(十一月)**。冬至是廿四節氣中最重要的,其它節氣對應的月可以有前後出入,之所以是冬至,因爲冬至是北半球正午日影最長的一日,比其它除了夏至外的節氣都更易觀測,而選冬至不選夏至可能是因爲冬季比較閒,無所事事的人就把冬至過成了一個重大節日,就顯得比夏至重要了。\n冬月定了,到下個冬月之間的月就按順序取名,如果中間正好有11個整月,那麼完美。但一年365.24日中平均有12.37個平均爲29.53日的月,有時候兩個冬月之間有12個整月,多了一個月,就得置閏月調整,這就麻煩了。想了一想,選了廿四節氣中的十二個叫中氣,兩個冬至之間必定有且只有11個中氣,如果有12個月,必定至少有一個月是無中氣的,這倒霉月就叫閏月。如果碰巧有的月佔了兩個中氣,就可能有兩個無中氣月,不能都閏了。總結就是:**兩個冬至之間若有13個朔,則首個無中氣月爲閏月**。\n# 既然是天文曆,會不會時區不同,計算出的日期也不同呢\n會的。\n華曆的定義是從天象來的,古人都在東亞,走不出多遠,天象都差不多;而現在視野開闊了,有了全球的概念,問題就複雜了。譬如長安23日早8點朔,23日即初一;而在紐約是22日晚7點朔,22日爲初一。因此同樣使用這套曆法的中國、韓國、越南曆,因爲時差的緣故,可能初一的日期就略有不同。\n初一有前後一日的出入,閏月的出入就更大了。中氣的平均間隔是30.44日,月平均長29.53日,相差不大,無中氣月的前後必定緊鄰前後兩個中氣。初一稍有不同,則哪個月無中氣就會有巨大差別,前後可以相差4個月。一日的出入可以接受,四個月的出入就難以接受了。\n所以這裏提供了另一種置閏法:中氣包含的計算規則,由初一至下個初一之間,改爲朔(精確時刻)至下個朔之間。隨時區不同,中氣可能落在不同日期,但朔時刻與中氣時刻之間的先後關係是不隨時區而變化的。此即「**精確至時刻**」選項,默認不開啓,需手動打開。\n# 時、刻又是甚麼\n午時三刻是眾所難忘的台詞,午時三刻究竟係幾點?時與刻是何關係,想必是常見的疑問。其實時與刻是兩種全然不同的計時方法。\n十二辰本是天上十二個星域,用以記年的,最古老的時辰並不固定是十二個,有十個者,也有十六個者,時長亦不定,而以自然現象或作息命名,如旦、昏、朝食、人定……。最早的精確時計是漏,漏上畫好刻。**一天分爲百刻,一刻合今14分24秒**。但百刻能把人眼看花,古人把十二辰用以計時,同時結合了百刻,出現了某時某刻的說法。即在百刻之上同時畫上十二個時辰,在過了某個時辰後就只數該時辰後多少刻。如此大大減輕了眼睛的負擔,再也不會數錯了。\n但問題來了,時辰之間間隔120分鐘,刻之間間隔14分24秒,不能整除。所以子、卯、午、酉四時辰與刻完美重合,其它時辰則不重合。而且時辰之後第一刻所代表的時長並不相同。子、卯、午、酉後第一刻是完整的一刻,其它時辰後第一刻則不完整。一小時60分鐘與一刻14分24秒的最大公約數爲2分24秒,是爲一小刻,一刻內有6小刻,在最內輪中畫出了小刻。\n有一點需要明確的是,古代的時辰是一個時刻,非時段,子時就是0:00那一時刻,而並非前一日23:00至後一日1:00那兩小時。子時三刻指子時後又過了三刻,而不是子時中的第三刻。\n至於爲何時辰會有一段時間,如子時爲23:00-1:00的印象?簡單來說就是隨著時計進步,出現了一種顯示時辰牌的時鐘,12:00時「午時」出現在窗口正中,而時辰牌不可能突然憑空出現,所以從11:00開始,「午時」牌出現在窗口角落,12:00在正中,13:00離開視線,這一段時間被叫做午時。在這之後,一個時辰被分成兩小時,前一小時爲某時「初」,後一小時爲某時「正」。\n# 真太陽時和標準時\n當今計時使用時區,譬如使用東八區時,正午就是東經120°處之正午,凡不在東經120°之處者,真正的正午時間並不是標準時的12時,此間有**經度時差**。此外,因地球繞日所行非圓,在近日點附近繞行更速,此時一日略長於平均;而於遠日點附近繞行更徐,一日略餖於平均,這也會影響正午時刻,這個差值叫**真平時差**。\n標準時即日常所用之時,而真太陽時則係校正此二項差值後之時。真太陽時的午正即當日太陽行經最高點之時,子正即太陽處於地球背後正對面之時。標準時的午正、子正則並無特別天文含義。\n# 年輪上的色塊是何物\n在華曆中,除了計日、計時,**五行星位置(辰、太白、熒惑、填、歲)**也是必備內容。當今有了現代天文學,行星和日月行跡都能精確計算了。其中填星和歲星位置曾在古代用於計年,如歲在大荒落、歲在辰,歲繞行太陽一週11.86年,約爲12年,故歲星紀年演變爲地支紀年,填星繞行一週29.5年,填與歲合併,約60年一週期,由此誕生演用至今的干支紀年。\n年輪上有**6個色塊(5星+月)**。24節氣既是日期,也是天球上的位置,如「清明」即清明時刻太陽所處的黃道位置,而歲星在清明即歲星在同一個黃道位置。辰星與太白星因處於地球軌道內,因此它們總是在太陽(即日輪進度條末位)附近,熒惑、填、歲則未必。而位於太陽前(即年輪上虚色部分)的星會在日出前升空,日入前入地;位於太陽後(即實色部份)的星會在日出後升空,日入後入地。\n# 月輪上的色塊是何物\n一般有4種:**朔、望、節、氣**,具體顏色可於設置內調整。若選精確至時刻置閏法,則月輪始自朔之刻,此時不標朔,只餘另三種色塊。望刻所在月最圓,可多觀察幾個月,看月圓究竟是十五還是十六,抑或是十七?氣與閏月息息相關,無氣之月一般是閏月。節氣色塊可與年輪上所標24節氣相對應。\n當接近朔、望、節、氣時刻時,同樣的色塊也會出現在日輪和時輪上,以便更精準定時。這四種色塊出現於日、時輪時位於輪**外側**。\n# 日月出入時刻\n日出入時刻對古人來說非常重要,與廿四節氣同屬對農事最重要的部份,係華曆中不可或缺的。\n如果打開了定位,則可計算當地日、月出入時刻,顯示於日輪**內側**,當接近某一時刻時,相同色塊亦會出現於時輪上,同樣位於內側。此類色塊共有7種:**日出、日中、日入、夜半、月出、月中、月入**,具體顏色亦可經設置調整。若開啓了真太陽時,因午正即日中、子正即半夜,此二類不再標出。\n# 名詞簡介\n**節氣**其實並無特別名稱,但爲與中氣相區別,此處指節氣中的奇数。含:大雪、小寒、立春、驚蟄(啓蟄)、清明、立夏、芒種、小暑、立秋、白露、寒露、立冬,共12個。驚蟄(啓蟄)、清明二節在後漢之前爲中氣,後漢元和二年分別與雨水、穀雨對調。\n**中氣**指節氣中的偶數。含:冬至、大寒、雨水、春分、穀雨、小滿、夏至、大暑、處暑、秋分、霜降、小雪,共12個。氣是華曆中重要的部份,無中氣月與閏月有關(詳見「華曆是甚麼」條)。\n**朔**指月與日共處同一天球經度之時刻,因此月與日同出同入,月華爲日光所蔽,故無月。日食發生於此刻。\n**望**指月運行至日之正對面(地球位於二者之間),日入時月出,日出時月入,故整夜可見月。月食發生於此刻。\n**五星**之古名與今不同。辰星即今之水星,又單名「星」;太白星即今之金星,以其爲天空最亮之星,故名太白;熒惑即今之火星;歲星即今之木星,太歲乃與歲星相關但不同的概念;填星又作鎮星,即今之土星。" + "value" : "# 何以要做此華曆?\n華曆即**中華日曆**。華曆在生活中還有用嗎?其實基本無用了,但作爲一種文化傳承,依然可以作爲精美的裝點。見慣了千篇一律,透露出十幾年前那既不傳統,又不現代的萬年曆,我就一直想做一款現代的華曆,於是就做了。\n靈感取自錶,把年月也倣似日、時一般做成輪。如此,年月日時皆一目瞭然,一年中的廿四節氣、大小月、閏月亦能聚在一盤,直觀呈現。\n# 華曆是甚麼?\n華曆是**陰陽曆**,亦係天文曆,一切皆以天文定,理念樸素,自有美感;但亦因此而難於計算,所幸在現代技術面前,計算不再是問題。以前每每感念新年年年不同,日曆下方的小字上的華曆日期也無甚規律,可當深入研究後才發現它的規則是如此簡單,而所引出的計算又是無比複雜。\n陰陽曆中的「陰」指的就係月了,新月,即月與日經度重疊的那一刻(如緯度亦重疊則爲日蝕),所在之日爲初一。因爲日與月同經,同升共落,那一日看不見月,這是非常易於觀測的天象,而月圓則沒那麼精確,圓一點、缺一點,幅度不大時並不明顯,古人以新月定初一是很樸素的。所以第一條規則即係:**新月所在之日爲初一,初一至下一初一前一日爲一月**。\n陰陽曆中的「陽」指的係日,月是以月定的,但沒有定這個月是幾月。給月定名是靠太陽完成的。爲了讓同樣的月總是處在類似的季節,便有了第二條規定:**冬至必定在冬月(十一月)**。冬至是廿四節氣中最重要的,其它節氣對應的月可以有前後出入,之所以是冬至,因爲冬至是北半球正午日影最長的一日,比其它除了夏至外的節氣都更易觀測,而選冬至不選夏至可能是因爲冬季比較閒,無所事事的人就把冬至過成了一個重大節日,就顯得比夏至重要了。\n冬月定了,到下個冬月之間的月就按順序取名,如果中間正好有11個整月,那麼完美。但一年365.24日中平均有12.37個平均爲29.53日的月,有時候兩個冬月之間有12個整月,多了一個月,就得置閏月調整,這就麻煩了。想了一想,選了廿四節氣中的十二個叫中氣,兩個冬至之間必定有且只有11個中氣,如果有12個月,必定至少有一個月是無中氣的,這倒霉月就叫閏月。如果碰巧有的月佔了兩個中氣,就可能有兩個無中氣月,不能都閏了。總結就是:**兩個冬至之間若有13個朔,則首個無中氣月爲閏月**。\n# 既然是天文曆,會不會時區不同,計算出的日期也不同呢\n會的。\n華曆的定義是從天象來的,古人都在東亞,走不出多遠,天象都差不多;而現在視野開闊了,有了全球的概念,問題就複雜了。譬如長安23日早8點朔,23日即初一;而在紐約是22日晚7點朔,22日爲初一。因此同樣使用這套曆法的中國、韓國、越南曆,因爲時差的緣故,可能初一的日期就略有不同。\n初一有前後一日的出入,閏月的出入就更大了。中氣的平均間隔是30.44日,月平均長29.53日,相差不大,無中氣月的前後必定緊鄰前後兩個中氣。初一稍有不同,則哪個月無中氣就會有巨大差別,前後可以相差4個月。一日的出入可以接受,四個月的出入就難以接受了。\n所以這裏提供了另一種置閏法:中氣包含的計算規則,由初一至下個初一之間,改爲朔(精確時刻)至下個朔之間。隨時區不同,中氣可能落在不同日期,但朔時刻與中氣時刻之間的先後關係是不隨時區而變化的。此即「**精確至時刻**」選項,默認不開啓,需手動打開。\n# 時、刻又是甚麼\n午時三刻是眾所難忘的台詞,午時三刻究竟係幾點?時與刻是何關係,想必是常見的疑問。其實時與刻是兩種全然不同的計時方法。\n十二辰本是天上十二個星域,用以記年的,最古老的時辰並不固定是十二個,有十個者,也有十六個者,時長亦不定,而以自然現象或作息命名,如旦、昏、朝食、人定……。最早的精確時計是漏,漏上畫好刻。**一天分爲百刻,一刻合今14分24秒**。但百刻能把人眼看花,古人把十二辰用以計時,同時結合了百刻,出現了某時某刻的說法。即在百刻之上同時畫上十二個時辰,在過了某個時辰後就只數該時辰後多少刻。如此大大減輕了眼睛的負擔,再也不會數錯了。\n但問題來了,時辰之間間隔120分鐘,刻之間間隔14分24秒,不能整除。所以子、卯、午、酉四時辰與刻完美重合,其它時辰則不重合。而且時辰之後第一刻所代表的時長並不相同。子、卯、午、酉後第一刻是完整的一刻,其它時辰後第一刻則不完整。一小時60分鐘與一刻14分24秒的最大公約數爲2分24秒,是爲一小刻,一刻內有6小刻,在最內輪中畫出了小刻。\n有一點需要明確的是,古代的時辰是一個時刻,非時段,子時就是0:00那一時刻,而並非前一日23:00至後一日1:00那兩小時。子時三刻指子時後又過了三刻,而不是子時中的第三刻。\n至於爲何時辰會有一段時間,如子時爲23:00-1:00的印象?簡單來說就是隨著時計進步,出現了一種顯示時辰牌的時鐘,12:00時「午時」出現在窗口正中,而時辰牌不可能突然憑空出現,所以從11:00開始,「午時」牌出現在窗口角落,12:00在正中,13:00離開視線,這一段時間被叫做午時。在這之後,一個時辰被分成兩小時,前一小時爲某時「初」,後一小時爲某時「正」。\n# 真太陽時和標準時\n當今計時使用時區,譬如使用東八區時,正午就是東經120°處之正午,凡不在東經120°之處者,真正的正午時間並不是標準時的12時,此間有**經度時差**。此外,因地球繞日所行非圓,在近日點附近繞行更速,此時一日略長於平均;而於遠日點附近繞行更徐,一日略餖於平均,這也會影響正午時刻,這個差值叫**真平時差**。\n標準時即日常所用之時,而真太陽時則係校正此二項差值後之時。真太陽時的午正即當日太陽行經最高點之時,子正即太陽處於地球背後正對面之時。標準時的午正、子正則並無特別天文含義。\n# 年輪上的色塊是何物\n在華曆中,除了計日、計時,**五行星位置(辰、太白、熒惑、填、歲)**也是必備內容。當今有了現代天文學,行星和日月行跡都能精確計算了。其中填星和歲星位置曾在古代用於計年,如歲在大荒落、歲在辰,歲繞行太陽一週11.86年,約爲12年,故歲星紀年演變爲地支紀年,填星繞行一週29.5年,填與歲合併,約60年一週期,由此誕生演用至今的干支紀年。\n年輪上有**6個色塊(5星+月)**。24節氣既是日期,也是天球上的位置,如「清明」即清明時刻太陽所處的黃道位置,而歲星在清明即歲星在同一個黃道位置。辰星與太白星因處於地球軌道內,因此它們總是在太陽(即日輪進度條末位)附近,熒惑、填、歲則未必。而位於太陽前(即年輪上虚色部分)的星會在日出前升空,日入前入地;位於太陽後(即實色部份)的星會在日出後升空,日入後入地。\n# 月輪上的色塊是何物\n一般有4種:**朔、望、節、氣**,具體顏色可於設置內調整。若選精確至時刻置閏法,則月輪始自朔之刻,此時不標朔,只餘另三種色塊。望刻所在月最圓,可多觀察幾個月,看月圓究竟是十五還是十六,抑或是十七?氣與閏月息息相關,無氣之月一般是閏月。節氣色塊可與年輪上所標24節氣相對應。\n當接近朔、望、節、氣時刻時,同樣的色塊也會出現在日輪和時輪上,以便更精準定時。這四種色塊出現於日、時輪時位於輪**外側**。\n# 日月出入時刻\n日出入時刻對古人來說非常重要,與廿四節氣同屬對農事最重要的部份,係華曆中不可或缺的。\n如果打開了定位,則可計算當地日、月出入時刻,顯示於日輪**內側**,當接近某一時刻時,相同色塊亦會出現於時輪上,同樣位於內側。此類色塊共有7種:**日出、日中、日入、夜半、月出、月中、月入**,具體顏色亦可經設置調整。若開啓了真太陽時,因午正即日中、子正即半夜,此二類不再標出。\n# 名詞簡介\n**節氣**其實並無特別名稱,但爲與中氣相區別,此處指節氣中的奇数。含:大雪、小寒、立春、驚蟄(啓蟄)、清明、立夏、芒種、小暑、立秋、白露、寒露、立冬,共12個。驚蟄(啓蟄)、清明二節在後漢之前爲中氣,後漢元和二年分別與雨水、穀雨對調。\n**中氣**指節氣中的偶數。含:冬至、大寒、雨水、春分、穀雨、小滿、夏至、大暑、處暑、秋分、霜降、小雪,共12個。氣是華曆中重要的部份,無中氣月與閏月有關(詳見「華曆是甚麼」條)。\n**朔**指月與日共處同一天球經度之時刻,因此月與日同出同入,月華爲日光所蔽,故無月。日食發生於此刻。\n**望**指月運行至日之正對面(地球位於二者之間),日入時月出,日出時月入,故整夜可見月。月食發生於此刻。\n**五星**之古名與今不同。辰星即今之水星,又單名「星」;太白星即今之金星,以其爲天空最亮之星,故名太白;熒惑即今之火星;歲星即今之木星,太歲乃與歲星相關但不同的概念;填星又作鎮星,即今之土星。" } } } @@ -785,6 +785,23 @@ } } }, + "定製自己的專屬風格" : { + "comment" : "Welcome, setting - title", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Customize in Settings" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "定制自己的专属风格" + } + } + } + }, "容吾三思" : { "comment" : "Cancel Resetting Settings", "extractionState" : "manual", @@ -978,7 +995,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Click gear icon in Chinese Time sheet to enter settings, where you can make customization and read documentations" + "value" : "Click gear icon in Chinendar sheet to enter settings, where you can make customization and read documentations" } }, "zh-Hans" : { @@ -1023,6 +1040,23 @@ } } }, + "底輪" : { + "comment" : "Ring background", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ring Back" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "底轮" + } + } + } + }, "度" : { "localizations" : { "en" : { @@ -2309,12 +2343,12 @@ } }, "華曆" : { - "comment" : "Chinese Time", + "comment" : "Chinendar", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Chinese Time" + "value" : "Chinendar" } }, "zh-Hans" : { @@ -2331,7 +2365,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Chinese Time is in Status Bar, click to show the sheet" + "value" : "Chinendar is in Status Bar, click to show the sheet" } }, "zh-Hans" : { @@ -2428,13 +2462,30 @@ } } }, + "設置位於錶盤正下方,更改後自動保存。可調時間、在地、外觀等。另有更多有關華曆之介紹。" : { + "comment" : "Welcome, setting - detail", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings locate below, changes made therein will auto save. Customize for display time, location and layouts. Find more about Chinendar inside." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设置位于表盘正下方,更改后自动保存。可调时间、在地、外观等。另有更多有关华历之介绍。" + } + } + } + }, "設置更改後自動保存。可調時間、在地、外觀等。另有更多有關華曆之介紹。" : { "comment" : "Welcome, long press - detail", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Changes made to settings will auto save. Customize for display time, location and layouts. Find more about Chinese Time in Settings." + "value" : "Changes made to settings will auto save. Customize for display time, location and layouts. Find more about Chinendar inside." } }, "zh-Hans" : { @@ -2835,4 +2886,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file diff --git a/Shared/Setting/ColorSetting.swift b/Shared/Setting/ColorSetting.swift index 7de5b6f..3d1a19e 100644 --- a/Shared/Setting/ColorSetting.swift +++ b/Shared/Setting/ColorSetting.swift @@ -1,6 +1,6 @@ // // ColorSetting.swift -// Chinese Time iOS +// Chinendar // // Created by Leo Liu on 6/23/23. // @@ -30,56 +30,56 @@ struct ColorSetting: View { var body: some View { Form { Section(header: Text("五星", comment: "Planets")) { - HStack() { + HStack(spacing: 20) { ColorSettingCell(text: Text("辰", comment: "Mercury Indicator"), color: watchLayout.binding(\.planetIndicator[0])) - Spacer(minLength: 30) + Divider() ColorSettingCell(text: Text("太白", comment: "Venus Indicator"), color: watchLayout.binding(\.planetIndicator[1])) } - HStack() { + HStack(spacing: 20) { ColorSettingCell(text: Text("熒惑", comment: "Mars Indicator"), color: watchLayout.binding(\.planetIndicator[2])) - Spacer(minLength: 30) + Divider() ColorSettingCell(text: Text("歲", comment: "Jupiter Indicator"), color: watchLayout.binding(\.planetIndicator[3])) } - HStack() { + HStack(spacing: 20) { ColorSettingCell(text: Text("鎮", comment: "Saturn Indicator"), color: watchLayout.binding(\.planetIndicator[4])) - Spacer(minLength: 30) + Divider() ColorSettingCell(text: Text("月", comment: "Moon position Indicator"), color: watchLayout.binding(\.planetIndicator[5])) } } Section(header: Text("朔望節氣", comment: "Moon phase and Solor terms")) { - HStack { + HStack(spacing: 20) { ColorSettingCell(text: Text("朔", comment: "New Moon Indicator"), color: watchLayout.binding(\.eclipseIndicator)) - Spacer(minLength: 30) + Divider() ColorSettingCell(text: Text("望", comment: "Full Moon Indicator"), color: watchLayout.binding(\.fullmoonIndicator)) } - HStack { + HStack(spacing: 20) { ColorSettingCell(text: Text("節氣", comment: "Odd Solar Term Indicator"), color: watchLayout.binding(\.oddStermIndicator)) - Spacer(minLength: 30) + Divider() ColorSettingCell(text: Text("中氣", comment: "Even Solar Term Indicator"), color: watchLayout.binding(\.evenStermIndicator)) } } Section(header: Text("日出入", comment: "Sunrise & Sunset Indicators")) { - HStack { + HStack(spacing: 20) { ColorSettingCell(text: Text("日出", comment: "Sunrise Indicator"), color: watchLayout.binding(\.sunPositionIndicator[1])) - Spacer(minLength: 30) + Divider() ColorSettingCell(text: Text("日中", comment: "Noon Indicator"), color: watchLayout.binding(\.sunPositionIndicator[2])) } - HStack { + HStack(spacing: 20) { ColorSettingCell(text: Text("日入", comment: "Sunset Indicator"), color: watchLayout.binding(\.sunPositionIndicator[3])) - Spacer(minLength: 30) + Divider() ColorSettingCell(text: Text("夜中", comment: "Midnight Indicator"), color: watchLayout.binding(\.sunPositionIndicator[0])) } } Section(header: Text("月出入", comment: "Moonrise & Moonset")) { - HStack { + HStack(spacing: 20) { ColorSettingCell(text: Text("月出", comment: "Moonrise Indicator"), color: watchLayout.binding(\.moonPositionIndicator[0])) - Spacer(minLength: 30) + Divider() ColorSettingCell(text: Text("月中", comment: "Lunar noon Indicator"), color: watchLayout.binding(\.moonPositionIndicator[1])) } - HStack { + HStack(spacing: 20) { ColorSettingCell(text: Text("月入", comment: "Moonset Indicator"), color: watchLayout.binding(\.moonPositionIndicator[2])) } } diff --git a/Shared/Setting/Datetime.swift b/Shared/Setting/Datetime.swift index 902a320..b3cd36a 100644 --- a/Shared/Setting/Datetime.swift +++ b/Shared/Setting/Datetime.swift @@ -1,6 +1,6 @@ // // Datetime.swift -// Chinese Time iOS +// Chinendar // // Created by Leo Liu on 6/23/23. // @@ -131,28 +131,11 @@ fileprivate struct TimeZoneSelection: Equatable { func updateTimeZone() { chineseCalendar?.update(time: watchSetting?.displayTime ?? .now, timezone: watchSetting?.timezone ?? Calendar.current.timeZone, location: chineseCalendar?.location) -#if os(macOS) - updateStatusBar() -#endif } func updateTime() { chineseCalendar?.update(time: watchSetting?.displayTime ?? .now) -#if os(macOS) - updateStatusBar() -#endif } - -#if os(macOS) - @MainActor - func updateStatusBar() { - if let delegate = AppDelegate.instance, - let chineseCalendar = chineseCalendar, - let watchLayout = watchLayout { - delegate.updateStatusBar(dateText: delegate.statusBar(from: chineseCalendar, options: watchLayout)) - } - } -#endif } private func populateTimezones() -> DataTree { @@ -193,7 +176,7 @@ struct Datetime: View { HStack { Picker("太陽時", selection: $dateManager.apparentTime) { - let choice = if locationManager.enabled { + let choice = if (locationManager.enabled) || (watchLayout.location != nil) { [true, false] } else { [false] @@ -222,7 +205,7 @@ struct Datetime: View { NSLocalizedString("時區", comment: "Timezone section") } Section(header: Text(timezoneTitle)) { - HStack { + HStack(spacing: 10) { Picker("大區", selection: $dateManager.timeZoneSelection.primary) { ForEach(dateManager.timeZoneSelection.timeZones.nextLevel.map { $0.nodeName }, id: \.self) { tz in Text(tz.replacingOccurrences(of: "_", with: " ")) @@ -232,8 +215,8 @@ struct Datetime: View { .lineLimit(1) .animation(.default, value: dateManager.timeZoneSelection) if let tzList = dateManager.timeZoneSelection.timeZones[dateManager.timeZoneSelection.primary], tzList.count > 0 { -#if os(macOS) - Spacer(minLength: 20) +#if os(macOS) || os(visionOS) + Divider() #endif Picker("中區", selection: $dateManager.timeZoneSelection.secondary) { ForEach(tzList.nextLevel.map { $0.nodeName }, id: \.self) { tz in @@ -244,8 +227,8 @@ struct Datetime: View { .animation(.default, value: dateManager.timeZoneSelection) } if let tzList = dateManager.timeZoneSelection.timeZones[dateManager.timeZoneSelection.primary], let tzList2 = tzList[dateManager.timeZoneSelection.secondary], tzList2.count > 0 { -#if os(macOS) - Spacer(minLength: 20) +#if os(macOS) || os(visionOS) + Divider() #endif Picker("小區", selection: $dateManager.timeZoneSelection.tertiary) { ForEach(tzList2.nextLevel.map { $0.nodeName }, id: \.self) { tz in diff --git a/Shared/Setting/Documentation.swift b/Shared/Setting/Documentation.swift index fd2aa9b..7db48ee 100644 --- a/Shared/Setting/Documentation.swift +++ b/Shared/Setting/Documentation.swift @@ -1,6 +1,6 @@ // // Documentation.swift -// Chinese Time iOS +// Chinendar // // Created by Leo Liu on 6/23/23. // @@ -8,73 +8,77 @@ import SwiftUI extension MarkdownElement { -#if os(iOS) - typealias SysFont = UIFont -#elseif os(macOS) - typealias SysFont = NSFont -#endif var attributeContainer: AttributeContainer { - var container = AttributeContainer() - switch self { - case .heading: - container.font = .systemFont(ofSize: SysFont.systemFontSize * 1.05) - case .paragraph: - container.font = .systemFont(ofSize: SysFont.systemFontSize / 1.05) - } - return container + var container = AttributeContainer() + switch self { + case .heading: + container.font = .headline + case .paragraph: + container.font = .body + } + return container } } -struct Documentation: View { - struct Paragraph: Identifiable { - var id = UUID() - let title: AttributedString - let body: [AttributedString] - var show: Bool = false - } +fileprivate struct Paragraph: Identifiable { + var id = UUID() + let title: AttributedString + let body: [AttributedString] + var show: Bool = false +} + +struct ParagraphView: View { + @State fileprivate var article: Paragraph + var body: some View { + Section { + Button { + withAnimation { + article.show.toggle() + } + } label: { + HStack { + Text(article.title) + .frame(maxWidth: .infinity, alignment: .leading) + Image(systemName: article.show ? "chevron.up" : "chevron.down") + .transition(.scale) +#if os(iOS) || os(macOS) + .foregroundStyle(Color.accentColor) +#endif + } + } +#if os(iOS) || os(macOS) + .buttonStyle(.plain) +#elseif os(visionOS) + .buttonStyle(.automatic) +#endif + if article.show { + VStack(spacing: 10) { + ForEach(0..: View { formatter.minimumFractionDigits = 0 return formatter }()) +#if os(iOS) || os(visionOS) + .keyboardType(.decimalPad) +#endif .focused($isFocused) .autocorrectionDisabled() .onSubmit(of: .text) { commit() } - .onChange(of: isFocused) { _, newValue in - if !newValue { + .onChange(of: isFocused) { + if !isFocused { commit() } } @@ -52,14 +55,17 @@ struct LayoutSettingCell: View { .padding(.vertical, 5) #elseif os(macOS) .frame(height: 20) +#elseif os(visionOS) + .frame(height: 40) #endif .padding(.leading, 15) -#if os(iOS) +#if os(iOS) || os(visionOS) .background(.thickMaterial, in: RoundedRectangle(cornerRadius: 10)) + .contentShape(.hoverEffect, .rect(cornerRadius: 10, style: .continuous)) + .hoverEffect() #elseif os(macOS) .background(in: RoundedRectangle(cornerRadius: 10)) #endif - .padding(.trailing, 10) } } @@ -170,42 +176,47 @@ struct LayoutSettingCell: View { struct LayoutSetting: View { @Environment(\.watchLayout) var watchLayout @Environment(\.watchSetting) var watchSetting -#if os(macOS) +#if os(macOS) || os(visionOS) static let nameMapping = [ "space": NSLocalizedString("空格", comment: "Space separator"), "dot": NSLocalizedString("・", comment: "・"), "none": NSLocalizedString("無", comment: "No separator") ] @Environment(\.chineseCalendar) var chineseCalendar +#endif +#if os(macOS) @State var fontHandler = FontHandler() #endif var body: some View { Form { -#if os(macOS) +#if os(macOS) || os(visionOS) Section(header: Text("狀態欄", comment: "Status Bar setting")) { - HStack { + HStack(spacing: 20) { Toggle("日", isOn: watchLayout.binding(\.statusBar.date)) - Spacer(minLength: 20) + Divider() Toggle("時", isOn: watchLayout.binding(\.statusBar.time)) } - HStack { + HStack(spacing: 20) { Picker("節日", selection: watchLayout.binding(\.statusBar.holiday)) { ForEach(0...2, id: \.self) { Text(String($0)) } } - Spacer(minLength: 20) + Divider() Picker("讀號", selection: watchLayout.binding(\.statusBar.separator)) { ForEach(WatchLayout.StatusBar.Separator.allCases, id: \.self) { Text(LayoutSetting.nameMapping[$0.rawValue]!) } } } } +#endif +#if os(macOS) Section(header: Text("字體", comment: "Font selection")) { - HStack { + HStack(spacing: 20) { Picker("小字", selection: $fontHandler.textFontSelection) { ForEach(fontHandler.allFonts, id:\.self) { family in Text(family) } } + Divider() Picker("麤細", selection: $fontHandler.textFontMemberSelection) { ForEach(fontHandler.textFontMembers, id:\.self) { member in Text(member) @@ -214,13 +225,13 @@ struct LayoutSetting: View { .labelsHidden() } - HStack { + HStack(spacing: 20) { Picker("大字", selection: $fontHandler.centerFontSelection) { ForEach(fontHandler.allFonts, id:\.self) { family in Text(family) } } - + Divider() Picker("麤細", selection: $fontHandler.centerFontMemberSelection) { ForEach(fontHandler.centerFontMembers, id:\.self) { member in Text(member) @@ -229,13 +240,13 @@ struct LayoutSetting: View { .labelsHidden() } } - .onChange(of: fontHandler.textFont) { _, _ in + .onChange(of: fontHandler.textFont) { if let font = fontHandler.textFont { watchLayout.textFont = font } } - .onChange(of: fontHandler.centerFont) { _, _ in + .onChange(of: fontHandler.centerFont) { if let font = fontHandler.centerFont { watchLayout.centerFont = font } @@ -245,8 +256,8 @@ struct LayoutSetting: View { Section(header: Text("形", comment: "Shape")) { LayoutSettingCell(text: watchSetting.vertical ? Text("寬", comment: "Width") : Text("高", comment: "Height"), value: watchLayout.binding(\.watchSize.width)) { max(10.0, $0) } LayoutSettingCell(text: watchSetting.vertical ? Text("高", comment: "Height") : Text("寬", comment: "Width"), value: watchLayout.binding(\.watchSize.height)) { max(10.0, $0) } - LayoutSettingCell(text: Text("圓角比例", comment: "Corner radius ratio"), value: watchLayout.binding(\.cornerRadiusRatio)) { min(1.0, max(0.0, $0)) } - LayoutSettingCell(text: Text("陰影大小", comment: "Shadow size"), value: watchLayout.binding(\.shadowSize)) { min(0.1, max(0.0, $0)) } + SliderView(value: watchLayout.binding(\.cornerRadiusRatio), min: 0, max: 1, label: Text("圓角比例", comment: "Corner radius ratio")) + SliderView(value: watchLayout.binding(\.shadowSize), min: 0, max: 0.1, label: Text("陰影大小", comment: "Shadow size")) } Section(header: Text("字偏", comment: "Text Shift")) { LayoutSettingCell(text: Text("大字平移", comment: "Height"), value: watchLayout.binding(\.centerTextOffset)) @@ -254,28 +265,31 @@ struct LayoutSetting: View { LayoutSettingCell(text: Text("小字平移", comment: "Height"), value: watchLayout.binding(\.horizontalTextOffset)) LayoutSettingCell(text: Text("小字縱移", comment: "Height"), value: watchLayout.binding(\.verticalTextOffset)) } -#elseif os(macOS) +#elseif os(macOS) || os(visionOS) Section(header: Text("形", comment: "Shape")) { - HStack { +#if os(macOS) + HStack(spacing: 20) { LayoutSettingCell(text: Text("寬", comment: "Width"), value: watchLayout.binding(\.watchSize.width)) { max(10.0, $0) } completion: { AppDelegate.instance?.watchPanel.panelPosition() } + Divider() LayoutSettingCell(text: Text("高", comment: "Height"), value: watchLayout.binding(\.watchSize.height)) { max(10.0, $0) } completion: { AppDelegate.instance?.watchPanel.panelPosition() } } - HStack { - LayoutSettingCell(text: Text("圓角比例", comment: "Corner radius ratio"), value: watchLayout.binding(\.cornerRadiusRatio)) { min(1.0, max(0.0, $0)) } - LayoutSettingCell(text: Text("陰影大小", comment: "Shadow size"), value: watchLayout.binding(\.shadowSize)) { min(0.1, max(0.0, $0)) } - } +#endif + SliderView(value: watchLayout.binding(\.cornerRadiusRatio), min: 0, max: 1, label: Text("圓角比例", comment: "Corner radius ratio")) + SliderView(value: watchLayout.binding(\.shadowSize), min: 0, max: 0.1, label: Text("陰影大小", comment: "Shadow size")) } Section(header: Text("字偏", comment: "Text Shift")) { - HStack { + HStack(spacing: 20) { LayoutSettingCell(text: Text("大字平移", comment: "Height"), value: watchLayout.binding(\.centerTextOffset)) + Divider() LayoutSettingCell(text: Text("大字縱移", comment: "Height"), value: watchLayout.binding(\.centerTextHOffset)) } - HStack { + HStack(spacing: 20) { LayoutSettingCell(text: Text("小字平移", comment: "Height"), value: watchLayout.binding(\.horizontalTextOffset)) + Divider() LayoutSettingCell(text: Text("小字縱移", comment: "Height"), value: watchLayout.binding(\.verticalTextOffset)) } } @@ -292,11 +306,6 @@ struct LayoutSetting: View { .fontWeight(.semibold) } #elseif os(macOS) - .onChange(of: watchLayout.statusBar) { _, _ in - if let delegate = AppDelegate.instance { - delegate.updateStatusBar(dateText: delegate.statusBar(from: chineseCalendar, options: watchLayout)) - } - } .task { fontHandler.textFont = watchLayout.textFont fontHandler.centerFont = watchLayout.centerFont diff --git a/Shared/Setting/Location.swift b/Shared/Setting/Location.swift index 174fb90..eaa8c8c 100644 --- a/Shared/Setting/Location.swift +++ b/Shared/Setting/Location.swift @@ -1,6 +1,6 @@ // // Location.swift -// Chinese Time iOS +// Chinendar // // Created by Leo Liu on 6/24/23. // @@ -139,7 +139,7 @@ struct LocationSelection: Equatable { } } -#if os(macOS) +#if os(macOS) || os(visionOS) struct OnSubmitTextField: View { let title: LocalizedStringKey let formatter: NumberFormatter @@ -155,16 +155,39 @@ struct OnSubmitTextField: View { } var body: some View { - TextField(title, value: $tempValue, formatter: formatter) - .focused($isFocused) - .onSubmit(of: .text) { - value = tempValue - } - .onChange(of: isFocused) { _, newValue in - if !newValue { + HStack { + Text(title) + .lineLimit(1) + .frame(maxWidth: .infinity, alignment: .leading) + TextField("", value: $tempValue, formatter: formatter) + .labelsHidden() +#if os(iOS) || os(visionOS) + .keyboardType(.decimalPad) +#endif + .focused($isFocused) + .autocorrectionDisabled() + .onSubmit(of: .text) { value = tempValue } - } + .onChange(of: isFocused) { + if !isFocused { + value = tempValue + } + } +#if os(macOS) + .frame(height: 20) +#elseif os(visionOS) + .frame(height: 40) +#endif + .padding(.leading, 15) +#if os(visionOS) + .background(.thickMaterial, in: RoundedRectangle(cornerRadius: 10)) + .contentShape(.hoverEffect, .rect(cornerRadius: 10, style: .continuous)) + .hoverEffect() +#elseif os(macOS) + .background(in: RoundedRectangle(cornerRadius: 10)) +#endif + } } } #endif @@ -209,7 +232,7 @@ struct Location: View { NSLocalizedString("緯度", comment: "Latitude section") } Section(header: Text(latitudeTitle)) { - HStack { + HStack(spacing: 10) { #if os(iOS) Picker("度", selection: $locationData.latitudeSelection.degree) { ForEach(0...89, id: \.self) { value in @@ -229,7 +252,7 @@ struct Location: View { } } .animation(.default, value: locationData.latitudeSelection) -#elseif os(macOS) +#elseif os(macOS) || os(visionOS) OnSubmitTextField("度", value: $locationData.latitudeSelection.degree, formatter: { let formatter = NumberFormatter() formatter.maximumFractionDigits = 0 @@ -237,8 +260,7 @@ struct Location: View { formatter.minimum = 0 return formatter }()) - .frame(height: 20) - .background(in: RoundedRectangle(cornerRadius: 10)) + Divider() OnSubmitTextField("分", value: $locationData.latitudeSelection.minute, formatter: { let formatter = NumberFormatter() formatter.maximumFractionDigits = 0 @@ -246,8 +268,7 @@ struct Location: View { formatter.minimum = 0 return formatter }()) - .frame(height: 20) - .background(in: RoundedRectangle(cornerRadius: 10)) + Divider() OnSubmitTextField("秒", value: $locationData.latitudeSelection.second, formatter: { let formatter = NumberFormatter() formatter.maximumFractionDigits = 0 @@ -255,8 +276,7 @@ struct Location: View { formatter.minimum = 0 return formatter }()) - .frame(height: 20) - .background(in: RoundedRectangle(cornerRadius: 10)) + Divider() #endif Picker("北南", selection: $locationData.latitudeSelection.positive) { ForEach([true, false], id: \.self) { value in @@ -279,7 +299,7 @@ struct Location: View { NSLocalizedString("經度", comment: "Longitude section") } Section(header: Text(longitudeTitle)) { - HStack { + HStack(spacing: 10) { #if os(iOS) Picker("度", selection: $locationData.longitudeSelection.degree) { ForEach(0...179, id: \.self) { value in @@ -299,7 +319,7 @@ struct Location: View { } } .animation(.default, value: locationData.longitudeSelection) -#elseif os(macOS) +#elseif os(macOS) || os(visionOS) OnSubmitTextField("度", value: $locationData.longitudeSelection.degree, formatter: { let formatter = NumberFormatter() formatter.maximumFractionDigits = 0 @@ -307,8 +327,7 @@ struct Location: View { formatter.minimum = 0 return formatter }()) - .frame(height: 20) - .background(in: RoundedRectangle(cornerRadius: 10)) + Divider() OnSubmitTextField("分", value: $locationData.longitudeSelection.minute, formatter: { let formatter = NumberFormatter() formatter.maximumFractionDigits = 0 @@ -316,8 +335,7 @@ struct Location: View { formatter.minimum = 0 return formatter }()) - .frame(height: 20) - .background(in: RoundedRectangle(cornerRadius: 10)) + Divider() OnSubmitTextField("秒", value: $locationData.longitudeSelection.second, formatter: { let formatter = NumberFormatter() formatter.maximumFractionDigits = 0 @@ -325,8 +343,7 @@ struct Location: View { formatter.minimum = 0 return formatter }()) - .frame(height: 20) - .background(in: RoundedRectangle(cornerRadius: 10)) + Divider() #endif Picker("東西", selection: $locationData.longitudeSelection.positive) { ForEach([true, false], id: \.self) { value in @@ -348,8 +365,8 @@ struct Location: View { .task { locationData.setup(locationManager: locationManager, watchLayout: watchLayout) } - .onChange(of: locationData.location) { _, newValue in - chineseCalendar.update(time: watchSetting.displayTime ?? Date.now, location: newValue) + .onChange(of: locationData.location) { + chineseCalendar.update(time: watchSetting.displayTime ?? Date.now, location: locationData.location) } .navigationTitle(Text("經緯度", comment: "Geo Location section")) #if os(iOS) diff --git a/Shared/Setting/RingSetting.swift b/Shared/Setting/RingSetting.swift index 6799570..749e4c4 100644 --- a/Shared/Setting/RingSetting.swift +++ b/Shared/Setting/RingSetting.swift @@ -1,6 +1,6 @@ // -// SliderView.swift -// Chinese Time iOS +// RingSetting.swift +// Chinendar // // Created by Leo Liu on 6/23/23. // @@ -185,8 +185,12 @@ struct ColorNodeView: NSViewRepresentable { } struct GradientSliderView: View { +#if os(visionOS) + let barHeight: CGFloat = 10 +#else let barHeight: CGFloat = 5 -#if os(iOS) +#endif +#if os(iOS) || os(visionOS) let slideHeight: CGFloat = 18 let pickerSize: CGFloat = 14 #elseif os(macOS) @@ -226,7 +230,7 @@ struct GradientSliderView: View { .padding(.leading, 10) Toggle("", isOn: $viewGradient.isLoop) .labelsHidden() - .onChange(of: viewGradient.isLoop) { _, _ in + .onChange(of: viewGradient.isLoop) { gradient = viewGradient.export(allowLoop: allowLoop) } } @@ -237,8 +241,14 @@ struct GradientSliderView: View { LinearGradient(gradient: Gradient(stops: viewGradient.gradientStops), startPoint: .leading, endPoint: .trailing) .frame(height: barHeight) .cornerRadius(size.height / 2) - .position(x: size.width / 2 - padding, y: barHeight / 2) - .padding(.top, slideHeight - barHeight / 2) + .padding(.horizontal, padding + pickerSize / 2) + .padding(.vertical, slideHeight - barHeight / 2) +#if os(visionOS) + .contentShape( + .rect(cornerRadius: 16, style: .continuous) + ) + .hoverEffect() +#endif .gesture(tapGesture) ForEach(0.. CGPoint { var boundedPoint = point #if os(iOS) - let verticalOffset = -pickerSize + barHeight - 33 + let verticalOffset = -pickerSize + barHeight - 33 + (allowLoop ? 0 : 10) +#elseif os(visionOS) + let verticalOffset = -pickerSize + barHeight - 39 + (allowLoop ? 0 : 12) #elseif os(macOS) - let verticalOffset = -pickerSize + barHeight - 18 + let verticalOffset = -pickerSize + barHeight - 19 #endif boundedPoint.x = max(min(boundedPoint.x, bound.width - padding * 2), 0) boundedPoint.y = max(min(boundedPoint.y, bound.height + verticalOffset), verticalOffset) @@ -338,24 +353,27 @@ struct GradientSliderView: View { struct SliderView: View { @Binding var value: CGFloat @State var currentValue: CGFloat = 0 + let min: CGFloat + let max: CGFloat let label: Text var body: some View { -#if os(iOS) +#if os(iOS) || os(visionOS) VStack { HStack { label - TextField("", value: $currentValue, formatter: { + Spacer() + + let formatter: NumberFormatter = { let formatter = NumberFormatter() formatter.maximumFractionDigits = 2 formatter.minimumFractionDigits = 0 return formatter - }()) - .labelsHidden() - .disabled(true) - .multilineTextAlignment(.trailing) + }() + Text(formatter.string(from: NSNumber(value: currentValue)) ?? "") + .frame(maxWidth: 40, alignment: .trailing) } - Slider(value: $currentValue, in: 0.0...1.0) { editing in + Slider(value: $currentValue, in: min...max) { editing in if !editing { value = currentValue } @@ -369,22 +387,21 @@ struct SliderView: View { HStack { label .frame(maxWidth: 150, alignment: .leading) - Slider(value: $currentValue, in: 0.0...1.0) { editing in + Slider(value: $currentValue, in: min...max) { editing in if !editing { value = currentValue } } .labelsHidden() - TextField("", value: $currentValue, formatter: { + + let formatter: NumberFormatter = { let formatter = NumberFormatter() formatter.maximumFractionDigits = 2 formatter.minimumFractionDigits = 0 return formatter - }()) - .frame(maxWidth: 40) - .labelsHidden() - .disabled(true) - .multilineTextAlignment(.trailing) + }() + Text(formatter.string(from: NSNumber(value: currentValue)) ?? "") + .frame(maxWidth: 40, alignment: .trailing) } .onAppear { currentValue = value @@ -411,6 +428,7 @@ struct ThemedColorSettingCell: View { ColorPicker("", selection: $color) .labelsHidden() .padding(.trailing, 10) + Divider() Text("暗", comment: "dark theme") .lineLimit(1) .frame(alignment: .trailing) @@ -433,10 +451,12 @@ struct RingSetting: View { var body: some View { Form { Section(header: Text("漸變色", comment: "Gradient Pickers")) { -#if os(iOS) +#if os(iOS) || os(visionOS) let height = 80.0 + let loopSize = 10.0 #else let height = 45.0 + let loopSize = 0.0 #endif GradientSliderView(text: Text("年輪", comment: "Year Ring Gradient"), gradient: watchLayout.binding(\.firstRing), allowLoop: true) .frame(height: height) @@ -445,20 +465,42 @@ struct RingSetting: View { GradientSliderView(text: Text("日輪", comment: "Day Ring Gradient"), gradient: watchLayout.binding(\.thirdRing), allowLoop: true) .frame(height: height) GradientSliderView(text: Text("大字", comment: "Day Ring Gradient"), gradient: watchLayout.binding(\.centerFontColor), allowLoop: false) - .frame(height: height) + .frame(height: height - loopSize) } Section(header: Text("透明度", comment: "Transparency sliders")) { - SliderView(value: watchLayout.binding(\.shadeAlpha), label: Text("殘圈透明", comment: "Inactive ring opacity")) - SliderView(value: watchLayout.binding(\.majorTickAlpha), label: Text("大刻透明", comment: "Major Tick opacity")) - SliderView(value: watchLayout.binding(\.minorTickAlpha), label: Text("小刻透明", comment: "Minor Tick opacity")) + SliderView(value: watchLayout.binding(\.shadeAlpha), min: 0, max: 1, label: Text("殘圈透明", comment: "Inactive ring opacity")) + SliderView(value: watchLayout.binding(\.majorTickAlpha), min: 0, max: 1, label: Text("大刻透明", comment: "Major Tick opacity")) + SliderView(value: watchLayout.binding(\.minorTickAlpha), min: 0, max: 1, label: Text("小刻透明", comment: "Minor Tick opacity")) } Section(header: Text("明暗主題色", comment: "Watch face colors in light and dark themes")) { +#if os(visionOS) + HStack(spacing: 20) { + ColorSettingCell(text: Text("大刻色", comment: "Major tick color"), color: watchLayout.binding(\.majorTickColorDark)) + Divider() + ColorSettingCell(text: Text("小刻色", comment: "Major tick color"), color: watchLayout.binding(\.minorTickColorDark)) + } + HStack(spacing: 20) { + ColorSettingCell(text: Text("節氣刻", comment: "Major tick color"), color: watchLayout.binding(\.oddSolarTermTickColorDark)) + Divider() + ColorSettingCell(text: Text("中氣刻", comment: "Major tick color"), color: watchLayout.binding(\.evenSolarTermTickColorDark)) + } + HStack(spacing: 20) { + ColorSettingCell(text: Text("小字", comment: "Major tick color"), color: watchLayout.binding(\.fontColorDark)) + Divider() + ColorSettingCell(text: Text("核", comment: "Major tick color"), color: watchLayout.binding(\.innerColorDark)) + } + HStack(spacing: 20) { + ColorSettingCell(text: Text("底輪", comment: "Ring background"), color: watchLayout.binding(\.backColorDark)) + } +#else ThemedColorSettingCell(text: Text("大刻色", comment: "Major tick color"), color: watchLayout.binding(\.majorTickColor), darkColor: watchLayout.binding(\.majorTickColorDark)) ThemedColorSettingCell(text: Text("小刻色", comment: "Major tick color"), color: watchLayout.binding(\.minorTickColor), darkColor: watchLayout.binding(\.minorTickColorDark)) ThemedColorSettingCell(text: Text("節氣刻", comment: "Major tick color"), color: watchLayout.binding(\.oddSolarTermTickColor), darkColor: watchLayout.binding(\.oddSolarTermTickColorDark)) ThemedColorSettingCell(text: Text("中氣刻", comment: "Major tick color"), color: watchLayout.binding(\.evenSolarTermTickColor), darkColor: watchLayout.binding(\.evenSolarTermTickColorDark)) ThemedColorSettingCell(text: Text("小字", comment: "Major tick color"), color: watchLayout.binding(\.fontColor), darkColor: watchLayout.binding(\.fontColorDark)) ThemedColorSettingCell(text: Text("核", comment: "Major tick color"), color: watchLayout.binding(\.innerColor), darkColor: watchLayout.binding(\.innerColorDark)) + ThemedColorSettingCell(text: Text("底輪", comment: "Ring background"), color: watchLayout.binding(\.backColor), darkColor: watchLayout.binding(\.backColorDark)) +#endif } } .formStyle(.grouped) diff --git a/Shared/Setting/StatusState.swift b/Shared/Setting/StatusState.swift new file mode 100644 index 0000000..8c0914f --- /dev/null +++ b/Shared/Setting/StatusState.swift @@ -0,0 +1,32 @@ +// +// StatusState.swift +// Chinendar +// +// Created by Leo Liu on 11/27/23. +// + +import Foundation + +class StatusState: Equatable { + var location: CGPoint? + var date: Date? + var timezone: TimeZone? + var statusBar: WatchLayout.StatusBar + var calendarSetting: Int + + init(locationManager: LocationManager, watchLayout: WatchLayout, watchSetting: WatchSetting) { + location = locationManager.location ?? watchLayout.location + date = watchSetting.displayTime + timezone = watchSetting.timezone + statusBar = watchLayout.statusBar + calendarSetting = (watchLayout.globalMonth ? 2 : 0) + (watchLayout.apparentTime ? 1 : 0) + } + + static func == (lhs: StatusState, rhs: StatusState) -> Bool { + return lhs.location == rhs.location && + lhs.date == rhs.date && + lhs.timezone == rhs.timezone && + lhs.statusBar == rhs.statusBar && + lhs.calendarSetting == rhs.calendarSetting + } +} diff --git a/Shared/Setting/ThemesList.swift b/Shared/Setting/ThemesList.swift index 2f72e67..9d83a14 100644 --- a/Shared/Setting/ThemesList.swift +++ b/Shared/Setting/ThemesList.swift @@ -1,6 +1,6 @@ // -// SwiftUIView.swift -// Chinese Time iOS +// ThemesList.swift +// Chinendar // // Created by Leo Liu on 6/16/23. // @@ -134,7 +134,7 @@ struct ThemesList: View { } label: { Text(theme.name!) } -#if os(iOS) +#if os(iOS) || os(visionOS) .buttonStyle(.borderless) #elseif os(macOS) .buttonStyle(.bordered) @@ -148,7 +148,7 @@ struct ThemesList: View { .swipeActions(edge: .leading) { renameButton } -#elseif os(macOS) +#elseif os(macOS) || os(visionOS) .contextMenu { Button { target = theme @@ -161,8 +161,10 @@ struct ThemesList: View { .labelStyle(.titleAndIcon) deleteButton .labelStyle(.titleAndIcon) +#if os(macOS) saveButton .labelStyle(.titleAndIcon) +#endif } #endif Spacer() @@ -173,11 +175,13 @@ struct ThemesList: View { Text(theme.modifiedDate!, style: .date) .foregroundStyle(.secondary) } -#if os(macOS) +#if os(macOS) || os(visionOS) Menu { renameButton deleteButton +#if os(macOS) saveButton +#endif } label: { Image(systemName: "ellipsis") } @@ -215,7 +219,11 @@ struct ThemesList: View { Button(NSLocalizedString("容吾三思", comment: "Cancel adding Settings"), role: .cancel) { target = nil } Button(NSLocalizedString("吾意已決", comment: "Confirm Resetting Settings"), role: .destructive) { if let target = target, !target.isNil { +#if os(visionOS) + watchLayout.update(from: target.code!, updateSize: false) +#else watchLayout.update(from: target.code!) +#endif #if os(iOS) WatchConnectivityManager.shared.sendLayout(watchLayout.encode(includeOffset: false)) #elseif os(macOS) @@ -255,6 +263,11 @@ struct ThemesList: View { .buttonStyle(.borderless) .labelStyle(.titleAndIcon) } +#elseif os(visionOS) + .toolbar { + newTheme + } + #elseif os(iOS) .navigationBarTitleDisplayMode(.inline) .toolbar { diff --git a/Shared/Utilities.swift b/Shared/Utilities.swift index a9bfce1..fa3f74f 100644 --- a/Shared/Utilities.swift +++ b/Shared/Utilities.swift @@ -1,6 +1,6 @@ // -// MarkdownParser.swift -// Chinese Time +// Utilities.swift +// Chinendar // // Created by Leo Liu on 4/29/23. // diff --git a/Shared/Views/HoverView.swift b/Shared/Views/HoverView.swift index c888389..2f02829 100644 --- a/Shared/Views/HoverView.swift +++ b/Shared/Views/HoverView.swift @@ -1,6 +1,6 @@ // -// NamedEntity.swift -// Chinese Time +// HoverView.swift +// Chinendar // // Created by Leo Liu on 6/26/23. // @@ -63,6 +63,11 @@ struct Hover: View { @State var prepared = true var body: some View { + + let shortEdge = min(watchLayout.watchSize.width, watchLayout.watchSize.height) + let longEdge = min(watchLayout.watchSize.width, watchLayout.watchSize.height) + let fontSize: CGFloat = min(shortEdge * 0.04, longEdge * 0.032) + GeometryReader { proxy in if let tapPos = tapPos, entityPresenting.activeNote.count > 0 { let idealPos = edgeSafePos(pos: tapPos, bounds: bounds, screen: proxy.size) @@ -70,22 +75,22 @@ struct Hover: View { HStack(alignment: .top) { ForEach(entityPresenting.activeNote) {note in VStack(spacing: 0) { - RoundedRectangle(cornerRadius: watchLayout.textFont.pointSize * 0.3) - .frame(width: watchLayout.textFont.pointSize, height: watchLayout.textFont.pointSize) + RoundedRectangle(cornerRadius: fontSize * 0.2) + .frame(width: fontSize, height: fontSize) .foregroundStyle(Color(cgColor: note.color)) - .padding(.vertical, 1) - Spacer(minLength: 3) - .frame(maxHeight: 3) + .padding(.vertical, fontSize * 0.08) + Spacer(minLength: fontSize * 0.2) + .frame(maxHeight: fontSize * 0.2) ForEach(Array(note.name), id: \.self) { char in Text(String(char)) - .font(.system(size: watchLayout.textFont.pointSize)) + .font(.system(size: fontSize)) .padding(0) } } } } - .padding(5) - .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 7, style: .continuous)) + .padding(fontSize * 0.3) + .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: fontSize * 0.5, style: .continuous)) .anchorPreference(key: BoundsPreferenceKey.self, value: .bounds) { proxy[$0] } .onPreferenceChange(BoundsPreferenceKey.self) { bounds = $0 } .position(idealPos) @@ -93,21 +98,21 @@ struct Hover: View { VStack(alignment: .leading) { ForEach(entityPresenting.activeNote) {note in HStack(spacing: 0) { - RoundedRectangle(cornerRadius: watchLayout.textFont.pointSize * 0.3) - .frame(width: watchLayout.textFont.pointSize, height: watchLayout.textFont.pointSize) + RoundedRectangle(cornerRadius: fontSize * 0.2) + .frame(width: fontSize, height: fontSize) .foregroundStyle(Color(cgColor: note.color)) - .padding(.horizontal, 1) + .padding(.horizontal, fontSize * 0.08) .padding(.vertical, 0) - Spacer(minLength: 3) - .frame(maxWidth: 3) + Spacer(minLength: fontSize * 0.2) + .frame(maxWidth: fontSize * 0.2) Text(Locale.translation[note.name] ?? "") - .font(.system(size: watchLayout.textFont.pointSize)) + .font(.system(size: fontSize)) .padding(0) } } } - .padding(5) - .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 7, style: .continuous)) + .padding(fontSize * 0.3) + .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: fontSize * 0.5, style: .continuous)) .anchorPreference(key: BoundsPreferenceKey.self, value: .bounds) { proxy[$0] } .onPreferenceChange(BoundsPreferenceKey.self) { bounds = $0 } .position(idealPos) diff --git a/Shared/Views/RoundedRect.swift b/Shared/Views/RoundedRect.swift index 7dd2604..a7c8365 100644 --- a/Shared/Views/RoundedRect.swift +++ b/Shared/Views/RoundedRect.swift @@ -1,6 +1,6 @@ // // RoundedRect.swift -// Chinese Time +// Chinendar // // Created by Leo Liu on 5/3/23. // diff --git a/Shared/Views/SwiftUIUtilities.swift b/Shared/Views/SwiftUIUtilities.swift index 7e023c9..92636f7 100644 --- a/Shared/Views/SwiftUIUtilities.swift +++ b/Shared/Views/SwiftUIUtilities.swift @@ -1,6 +1,6 @@ // -// StartingPhase.swift -// Chinese Time +// SwiftUIUtilities.swift +// Chinendar // // Created by Leo Liu on 5/11/23. // diff --git a/Shared/Views/WatchFaceBasics.swift b/Shared/Views/WatchFaceBasics.swift index dff8f93..0c800a7 100644 --- a/Shared/Views/WatchFaceBasics.swift +++ b/Shared/Views/WatchFaceBasics.swift @@ -1,6 +1,6 @@ // -// WatchFace.swift -// ChineseTime Watch App +// WatchFaceBasics.swift +// Chinendar // // Created by Leo Liu on 5/4/23. // @@ -131,11 +131,12 @@ struct Ring: View { let pathWithAngle: CGPath let majorTickColor: CGColor let minorTickColor: CGColor + let backColor: CGColor let gradient: Gradient fileprivate let drawableTexts: [DrawableText] fileprivate let drawableMarks: [DrawableMark] - init(width: CGFloat, viewSize: CGSize, compact: Bool, ticks: ChineseCalendar.Ticks, startingAngle: CGFloat, angle: CGFloat, textFont: WatchFont, textColor: CGColor, alpha: CGFloat, majorTickAlpha: CGFloat, minorTickAlpha: CGFloat, majorTickColor: CGColor, minorTickColor: CGColor, gradientColor: WatchLayout.Gradient, outerRing: RoundedRect, marks: [Marks], shadowDirection: CGFloat, entityNotes: EntityNotes?, shadowSize: CGFloat, offset: CGSize = .zero) { + init(width: CGFloat, viewSize: CGSize, compact: Bool, ticks: ChineseCalendar.Ticks, startingAngle: CGFloat, angle: CGFloat, textFont: WatchFont, textColor: CGColor, alpha: CGFloat, majorTickAlpha: CGFloat, minorTickAlpha: CGFloat, majorTickColor: CGColor, minorTickColor: CGColor, backColor: CGColor, gradientColor: WatchLayout.Gradient, outerRing: RoundedRect, marks: [Marks], shadowDirection: CGFloat, entityNotes: EntityNotes?, shadowSize: CGFloat, offset: CGSize = .zero) { self.shortEdge = min(viewSize.width, viewSize.height) let longEdge = max(viewSize.width, viewSize.height) self.alpha = alpha @@ -143,6 +144,7 @@ struct Ring: View { self.minorTickAlpha = minorTickAlpha self.majorTickColor = majorTickColor self.minorTickColor = minorTickColor + self.backColor = backColor self.outerRing = outerRing self.shadowDirection = shadowDirection self.shadowSize = shadowSize @@ -210,7 +212,7 @@ struct Ring: View { ZStack { Path(outerRingPath) .fill(.clear) - .background(.windowBackground, in: Path(outerRingPath)) + .background(Color(cgColor: backColor), in: Path(outerRingPath)) Canvas { graphicsContext, size in var context = graphicsContext @@ -273,6 +275,7 @@ struct Ring: View { struct Core: View { let viewSize: CGSize let outerBound: RoundedRect + let innerColor: CGColor let backColor: CGColor let centerOffset: CGFloat let shadowDirection: CGFloat @@ -282,10 +285,11 @@ struct Core: View { let gradient: Gradient fileprivate let drawableTexts: [DrawableText] - init(viewSize: CGSize, compact: Bool, dateString: String, timeString: String, font: WatchFont, maxLength: Int, textColor: WatchLayout.Gradient, outerBound: RoundedRect, backColor: CGColor, centerOffset: CGFloat, shadowDirection: CGFloat, shadowSize: CGFloat) { + init(viewSize: CGSize, compact: Bool, dateString: String, timeString: String, font: WatchFont, maxLength: Int, textColor: WatchLayout.Gradient, outerBound: RoundedRect, innerColor: CGColor, backColor: CGColor, centerOffset: CGFloat, shadowDirection: CGFloat, shadowSize: CGFloat) { self.viewSize = viewSize self.shortEdge = min(viewSize.width, viewSize.height) self.outerBound = outerBound + self.innerColor = innerColor self.backColor = backColor self.centerOffset = centerOffset self.shadowDirection = shadowDirection @@ -302,10 +306,10 @@ struct Core: View { ZStack { Path(outerBoundPath) .fill(.clear) - .background(.windowBackground, in: Path(outerBoundPath)) + .background(Color(cgColor: backColor), in: Path(outerBoundPath)) Canvas { context, _ in - context.fill(Path(outerBoundPath), with: .color(Color(cgColor: backColor))) + context.fill(Path(outerBoundPath), with: .color(Color(cgColor: innerColor))) var startPoint = CGPoint(x: viewSize.width/2, y: viewSize.height/2) var endPoint = startPoint diff --git a/Shared/Views/WatchFaceView.swift b/Shared/Views/WatchFaceView.swift index 2bcaaae..47b960c 100644 --- a/Shared/Views/WatchFaceView.swift +++ b/Shared/Views/WatchFaceView.swift @@ -1,6 +1,6 @@ // // WatchFaceView.swift -// Chinese Time Watch +// Chinendar // // Created by Leo Liu on 5/9/23. // @@ -100,8 +100,12 @@ struct Watch: View { static let frameOffset: CGFloat = 0.03 @Environment(\.colorScheme) var colorScheme +#if !os(visionOS) @Environment(\.showsWidgetContainerBackground) var showsWidgetContainerBackground @Environment(\.widgetRenderingMode) var widgetRenderingMode +#else + let showsWidgetContainerBackground = true +#endif @Environment(\.scaleEffectScale) var scaleEffectScale @Environment(\.scaleEffectAnchor) var scaleEffectAnchor let shrink: Bool @@ -137,18 +141,24 @@ struct Watch: View { var body: some View { +#if !os(visionOS) let watchLayout = switch widgetRenderingMode { case .fullColor: self.watchLayout default: self.watchLayout.monochrome } +#else + let watchLayout = self.watchLayout +#endif + let fourthRingColor = calSubhourGradient(watchLayout: watchLayout, chineseCalendar: chineseCalendar) let textColor = colorScheme == .dark ? watchLayout.fontColorDark : watchLayout.fontColor let majorTickColor = colorScheme == .dark ? watchLayout.majorTickColorDark : watchLayout.majorTickColor let minorTickColor = colorScheme == .dark ? watchLayout.minorTickColorDark : watchLayout.minorTickColor let coreColor = colorScheme == .dark ? watchLayout.innerColorDark : watchLayout.innerColor + let backColor = colorScheme == .dark ? watchLayout.backColorDark : watchLayout.backColor let shadowDirection = chineseCalendar.currentHourInDay GeometryReader { proxy in @@ -172,20 +182,20 @@ struct Watch: View { ZeroRing(width: ZeroRing.width * widthScale, viewSize: size, compact: compact, textFont: WatchFont(watchLayout.textFont), outerRing: outerBound, startingAngle: phase.zeroRing, oddTicks: chineseCalendar.oddSolarTerms.map { CGFloat($0) }, evenTicks: chineseCalendar.evenSolarTerms.map { CGFloat($0) }, oddColor: oddSTColor, evenColor: evenSTColor, oddTexts: ChineseCalendar.oddSolarTermChinese, evenTexts: ChineseCalendar.evenSolarTermChinese, offset: shift) } let _ = entityNotes?.reset() - Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.monthTicks, startingAngle: phase.firstRing, angle: chineseCalendar.currentDayInYear, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, gradientColor: watchLayout.firstRing, outerRing: firstRingOuter, marks: firstRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: showsWidgetContainerBackground ? watchLayout.shadowSize : 0.0, offset: shift) + Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.monthTicks, startingAngle: phase.firstRing, angle: chineseCalendar.currentDayInYear, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, backColor: backColor, gradientColor: watchLayout.firstRing, outerRing: firstRingOuter, marks: firstRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: showsWidgetContainerBackground ? watchLayout.shadowSize : 0.0, offset: shift) .scaleEffect(1 + scaleEffectScale * 0.25, anchor: scaleEffectAnchor) .animation(.spring(duration: 0.5, bounce: 0.6, blendDuration: 0.2), value: scaleEffectScale) - Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.dayTicks, startingAngle: phase.secondRing, angle: chineseCalendar.currentDayInMonth, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, gradientColor: watchLayout.secondRing, outerRing: secondRingOuter, marks: secondRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: watchLayout.shadowSize, offset: shift) + Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.dayTicks, startingAngle: phase.secondRing, angle: chineseCalendar.currentDayInMonth, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, backColor: backColor, gradientColor: watchLayout.secondRing, outerRing: secondRingOuter, marks: secondRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: watchLayout.shadowSize, offset: shift) .scaleEffect(1 + scaleEffectScale * 0.5, anchor: scaleEffectAnchor) .animation(.spring(duration: 0.5, bounce: 0.65, blendDuration: 0.2), value: scaleEffectScale) - Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.hourTicks, startingAngle: phase.thirdRing, angle: chineseCalendar.currentHourInDay, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, gradientColor: watchLayout.thirdRing, outerRing: thirdRingOuter, marks: thirdRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: watchLayout.shadowSize, offset: shift) + Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.hourTicks, startingAngle: phase.thirdRing, angle: chineseCalendar.currentHourInDay, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, backColor: backColor, gradientColor: watchLayout.thirdRing, outerRing: thirdRingOuter, marks: thirdRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: watchLayout.shadowSize, offset: shift) .scaleEffect(1 + scaleEffectScale * 0.75, anchor: scaleEffectAnchor) .animation(.spring(duration: 0.5, bounce: 0.7, blendDuration: 0.2), value: scaleEffectScale) - Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.subhourTicks, startingAngle: phase.fourthRing, angle: chineseCalendar.subhourInHour, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, gradientColor: fourthRingColor, outerRing: fourthRingOuter, marks: fourthRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: watchLayout.shadowSize, offset: shift) + Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.subhourTicks, startingAngle: phase.fourthRing, angle: chineseCalendar.subhourInHour, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, backColor: backColor, gradientColor: fourthRingColor, outerRing: fourthRingOuter, marks: fourthRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: watchLayout.shadowSize, offset: shift) .scaleEffect(1 + scaleEffectScale, anchor: scaleEffectAnchor) .animation(.spring(duration: 0.5, bounce: 0.75, blendDuration: 0.2), value: scaleEffectScale) let timeString = displaySubquarter ? chineseCalendar.timeString : (chineseCalendar.hourString + chineseCalendar.shortQuarterString) - Core(viewSize: size, compact: compact, dateString: chineseCalendar.dateString, timeString: timeString, font: WatchFont(watchLayout.centerFont), maxLength: 5, textColor: watchLayout.centerFontColor, outerBound: innerBound, backColor: coreColor, centerOffset: centerOffset, shadowDirection: shadowDirection, shadowSize: watchLayout.shadowSize) + Core(viewSize: size, compact: compact, dateString: chineseCalendar.dateString, timeString: timeString, font: WatchFont(watchLayout.centerFont), maxLength: 5, textColor: watchLayout.centerFontColor, outerBound: innerBound, innerColor: coreColor, backColor: backColor, centerOffset: centerOffset, shadowDirection: shadowDirection, shadowSize: watchLayout.shadowSize) .scaleEffect(1 + scaleEffectScale * 1.25, anchor: scaleEffectAnchor) .animation(.spring(duration: 0.5, bounce: 0.8, blendDuration: 0.2), value: scaleEffectScale) } @@ -199,8 +209,12 @@ struct DateWatch: View { @Environment(\.scaleEffectScale) var scaleEffectScale @Environment(\.scaleEffectAnchor) var scaleEffectAnchor @Environment(\.colorScheme) var colorScheme +#if !os(visionOS) @Environment(\.showsWidgetContainerBackground) var showsWidgetContainerBackground @Environment(\.widgetRenderingMode) var widgetRenderingMode +#else + let showsWidgetContainerBackground = true +#endif let shrink: Bool let displayZeroRing: Bool let compact: Bool @@ -226,17 +240,22 @@ struct DateWatch: View { var body: some View { +#if !os(visionOS) let watchLayout = switch widgetRenderingMode { case .fullColor: self.watchLayout default: self.watchLayout.monochrome } +#else + let watchLayout = self.watchLayout +#endif let textColor = colorScheme == .dark ? watchLayout.fontColorDark : watchLayout.fontColor let majorTickColor = colorScheme == .dark ? watchLayout.majorTickColorDark : watchLayout.majorTickColor let minorTickColor = colorScheme == .dark ? watchLayout.minorTickColorDark : watchLayout.minorTickColor let coreColor = colorScheme == .dark ? watchLayout.innerColorDark : watchLayout.innerColor + let backColor = colorScheme == .dark ? watchLayout.backColorDark : watchLayout.backColor let shadowDirection = chineseCalendar.currentHourInDay GeometryReader { proxy in @@ -258,14 +277,14 @@ struct DateWatch: View { ZeroRing(width: ZeroRing.width * widthScale, viewSize: size, compact: compact, textFont: WatchFont(watchLayout.textFont), outerRing: outerBound, startingAngle: phase.zeroRing, oddTicks: chineseCalendar.oddSolarTerms.map { CGFloat($0) }, evenTicks: chineseCalendar.evenSolarTerms.map { CGFloat($0) }, oddColor: oddSTColor, evenColor: evenSTColor, oddTexts: ChineseCalendar.oddSolarTermChinese, evenTexts: ChineseCalendar.evenSolarTermChinese) } let _ = entityNotes?.reset() - Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.monthTicks, startingAngle: phase.firstRing, angle: chineseCalendar.currentDayInYear, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, gradientColor: watchLayout.firstRing, outerRing: firstRingOuter, marks: firstRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: showsWidgetContainerBackground ? watchLayout.shadowSize : 0.0) + Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.monthTicks, startingAngle: phase.firstRing, angle: chineseCalendar.currentDayInYear, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, backColor: backColor, gradientColor: watchLayout.firstRing, outerRing: firstRingOuter, marks: firstRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: showsWidgetContainerBackground ? watchLayout.shadowSize : 0.0) .scaleEffect(1 + scaleEffectScale * 0.5, anchor: scaleEffectAnchor) .animation(.spring(duration: 0.5, bounce: 0.6, blendDuration: 0.2), value: scaleEffectScale) - Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.dayTicks, startingAngle: phase.secondRing, angle: chineseCalendar.currentDayInMonth, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, gradientColor: watchLayout.secondRing, outerRing: secondRingOuter, marks: secondRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: watchLayout.shadowSize) + Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.dayTicks, startingAngle: phase.secondRing, angle: chineseCalendar.currentDayInMonth, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, backColor: backColor, gradientColor: watchLayout.secondRing, outerRing: secondRingOuter, marks: secondRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: watchLayout.shadowSize) .scaleEffect(1 + scaleEffectScale * 0.75, anchor: scaleEffectAnchor) .animation(.spring(duration: 0.5, bounce: 0.7, blendDuration: 0.2), value: scaleEffectScale) - Core(viewSize: size, compact: compact, dateString: chineseCalendar.monthString, timeString: chineseCalendar.dayString, font: WatchFont(watchLayout.centerFont), maxLength: 3, textColor: watchLayout.centerFontColor, outerBound: innerBound, backColor: coreColor, centerOffset: centerOffset, shadowDirection: shadowDirection, shadowSize: watchLayout.shadowSize) + Core(viewSize: size, compact: compact, dateString: chineseCalendar.monthString, timeString: chineseCalendar.dayString, font: WatchFont(watchLayout.centerFont), maxLength: 3, textColor: watchLayout.centerFontColor, outerBound: innerBound, innerColor: coreColor, backColor: backColor, centerOffset: centerOffset, shadowDirection: shadowDirection, shadowSize: watchLayout.shadowSize) .scaleEffect(1 + scaleEffectScale, anchor: scaleEffectAnchor) .animation(.spring(duration: 0.5, bounce: 0.8, blendDuration: 0.2), value: scaleEffectScale) } @@ -279,8 +298,12 @@ struct TimeWatch: View { @Environment(\.scaleEffectScale) var scaleEffectScale @Environment(\.scaleEffectAnchor) var scaleEffectAnchor @Environment(\.colorScheme) var colorScheme +#if !os(visionOS) @Environment(\.showsWidgetContainerBackground) var showsWidgetContainerBackground @Environment(\.widgetRenderingMode) var widgetRenderingMode +#else + let showsWidgetContainerBackground = true +#endif let shrink: Bool let displayZeroRing: Bool let displaySubquarter: Bool @@ -307,18 +330,25 @@ struct TimeWatch: View { } var body: some View { + +#if !os(visionOS) let watchLayout = switch widgetRenderingMode { case .fullColor: self.watchLayout default: self.watchLayout.monochrome } +#else + let watchLayout = self.watchLayout +#endif + let fourthRingColor = calSubhourGradient(watchLayout: watchLayout, chineseCalendar: chineseCalendar) let textColor = colorScheme == .dark ? watchLayout.fontColorDark : watchLayout.fontColor let majorTickColor = colorScheme == .dark ? watchLayout.majorTickColorDark : watchLayout.majorTickColor let minorTickColor = colorScheme == .dark ? watchLayout.minorTickColorDark : watchLayout.minorTickColor let coreColor = colorScheme == .dark ? watchLayout.innerColorDark : watchLayout.innerColor + let backColor = colorScheme == .dark ? watchLayout.backColorDark : watchLayout.backColor let shadowDirection = chineseCalendar.currentHourInDay GeometryReader { proxy in @@ -334,15 +364,15 @@ struct TimeWatch: View { ZStack { let _ = entityNotes?.reset() - Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.hourTicks, startingAngle: phase.thirdRing, angle: chineseCalendar.currentHourInDay, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, gradientColor: watchLayout.thirdRing, outerRing: firstRingOuter, marks: thirdRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: showsWidgetContainerBackground ? watchLayout.shadowSize : 0.0) + Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.hourTicks, startingAngle: phase.thirdRing, angle: chineseCalendar.currentHourInDay, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, backColor: backColor, gradientColor: watchLayout.thirdRing, outerRing: firstRingOuter, marks: thirdRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: showsWidgetContainerBackground ? watchLayout.shadowSize : 0.0) .scaleEffect(1 + scaleEffectScale * 0.5, anchor: scaleEffectAnchor) .animation(.spring(duration: 0.5, bounce: 0.6, blendDuration: 0.2), value: scaleEffectScale) - Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.subhourTicks, startingAngle: phase.fourthRing, angle: chineseCalendar.subhourInHour, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, gradientColor: fourthRingColor, outerRing: secondRingOuter, marks: fourthRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: watchLayout.shadowSize) + Ring(width: Ring.paddedWidth * widthScale, viewSize: size, compact: compact, ticks: chineseCalendar.subhourTicks, startingAngle: phase.fourthRing, angle: chineseCalendar.subhourInHour, textFont: WatchFont(watchLayout.textFont), textColor: textColor, alpha: watchLayout.shadeAlpha, majorTickAlpha: watchLayout.majorTickAlpha, minorTickAlpha: watchLayout.minorTickAlpha, majorTickColor: majorTickColor, minorTickColor: minorTickColor, backColor: backColor, gradientColor: fourthRingColor, outerRing: secondRingOuter, marks: fourthRingMarks, shadowDirection: shadowDirection, entityNotes: entityNotes, shadowSize: watchLayout.shadowSize) .scaleEffect(1 + scaleEffectScale * 0.75, anchor: scaleEffectAnchor) .animation(.spring(duration: 0.5, bounce: 0.7, blendDuration: 0.2), value: scaleEffectScale) let timeString = displaySubquarter ? chineseCalendar.quarterString : chineseCalendar.shortQuarterString - Core(viewSize: size, compact: compact, dateString: chineseCalendar.hourString, timeString: timeString, font: WatchFont(watchLayout.centerFont), maxLength: 3, textColor: watchLayout.centerFontColor, outerBound: innerBound, backColor: coreColor, centerOffset: centerOffset, shadowDirection: shadowDirection, shadowSize: watchLayout.shadowSize) + Core(viewSize: size, compact: compact, dateString: chineseCalendar.hourString, timeString: timeString, font: WatchFont(watchLayout.centerFont), maxLength: 3, textColor: watchLayout.centerFontColor, outerBound: innerBound, innerColor: coreColor, backColor: backColor, centerOffset: centerOffset, shadowDirection: shadowDirection, shadowSize: watchLayout.shadowSize) .scaleEffect(1 + scaleEffectScale, anchor: scaleEffectAnchor) .animation(.spring(duration: 0.5, bounce: 0.8, blendDuration: 0.2), value: scaleEffectScale) } diff --git a/Shared/WatchConnectivity.swift b/Shared/WatchConnectivity.swift index 3ed44d4..2ba0235 100644 --- a/Shared/WatchConnectivity.swift +++ b/Shared/WatchConnectivity.swift @@ -1,6 +1,6 @@ // // WatchConnectivity.swift -// Chinese Time +// Chinendar // // Created by Leo Liu on 5/5/23. // diff --git a/Vision/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json b/Vision/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..6a54b2a --- /dev/null +++ b/Vision/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "filename" : "back.png", + "idiom" : "vision", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vision/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/back.png b/Vision/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/back.png new file mode 100644 index 0000000..8d620d7 Binary files /dev/null and b/Vision/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/back.png differ diff --git a/Vision/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json b/Vision/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Vision/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vision/Assets.xcassets/AppIcon.solidimagestack/Contents.json b/Vision/Assets.xcassets/AppIcon.solidimagestack/Contents.json new file mode 100644 index 0000000..950af4d --- /dev/null +++ b/Vision/Assets.xcassets/AppIcon.solidimagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.solidimagestacklayer" + }, + { + "filename" : "Middle.solidimagestacklayer" + }, + { + "filename" : "Back.solidimagestacklayer" + } + ] +} diff --git a/Vision/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json b/Vision/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..00bb5d1 --- /dev/null +++ b/Vision/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "filename" : "top.png", + "idiom" : "vision", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vision/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/top.png b/Vision/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/top.png new file mode 100644 index 0000000..a306b2c Binary files /dev/null and b/Vision/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/top.png differ diff --git a/Vision/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json b/Vision/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Vision/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vision/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json b/Vision/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..c8c91f3 --- /dev/null +++ b/Vision/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "filename" : "middle.png", + "idiom" : "vision", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vision/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/middle.png b/Vision/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/middle.png new file mode 100644 index 0000000..8e11b34 Binary files /dev/null and b/Vision/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/middle.png differ diff --git a/Vision/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json b/Vision/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Vision/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vision/Assets.xcassets/Contents.json b/Vision/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Vision/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vision/Assets.xcassets/Image.imageset/Contents.json b/Vision/Assets.xcassets/Image.imageset/Contents.json new file mode 100644 index 0000000..545a688 --- /dev/null +++ b/Vision/Assets.xcassets/Image.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "watch icon1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "watch icon2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "watch icon3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vision/Assets.xcassets/Image.imageset/watch icon1x.png b/Vision/Assets.xcassets/Image.imageset/watch icon1x.png new file mode 100644 index 0000000..6004c95 Binary files /dev/null and b/Vision/Assets.xcassets/Image.imageset/watch icon1x.png differ diff --git a/Vision/Assets.xcassets/Image.imageset/watch icon2x.png b/Vision/Assets.xcassets/Image.imageset/watch icon2x.png new file mode 100644 index 0000000..7cfab46 Binary files /dev/null and b/Vision/Assets.xcassets/Image.imageset/watch icon2x.png differ diff --git a/Vision/Assets.xcassets/Image.imageset/watch icon3x.png b/Vision/Assets.xcassets/Image.imageset/watch icon3x.png new file mode 100644 index 0000000..8a3687b Binary files /dev/null and b/Vision/Assets.xcassets/Image.imageset/watch icon3x.png differ diff --git a/iOS/ChineseTimeiOS.entitlements b/Vision/Chinendar.entitlements similarity index 100% rename from iOS/ChineseTimeiOS.entitlements rename to Vision/Chinendar.entitlements diff --git a/Vision/Info.plist b/Vision/Info.plist new file mode 100644 index 0000000..d7ad317 --- /dev/null +++ b/Vision/Info.plist @@ -0,0 +1,27 @@ + + + + + ITSAppUsesNonExemptEncryption + + LSHasLocalizedDisplayName + + UIAppFonts + + SourceHanSansKR-Heavy.otf + + UIApplicationSceneManifest + + UIApplicationPreferredDefaultSceneSessionRole + UIWindowSceneSessionRoleApplication + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + + UIBackgroundModes + + remote-notification + + + diff --git a/Vision/Layout.swift b/Vision/Layout.swift new file mode 100644 index 0000000..822f690 --- /dev/null +++ b/Vision/Layout.swift @@ -0,0 +1,107 @@ +// +// Layout.swift +// Chinendar +// +// Created by Leo Liu on 11/12/23. +// + +import SwiftUI +import Observation + +@Observable final class WatchLayout: MetaWatchLayout { + static let shared = WatchLayout() + + struct StatusBar: Equatable { + + enum Separator: String, CaseIterable { + case space, dot, none + var symbol: String { + switch self { + case .space: " " + case .dot: "・" + case .none: "" + } + } + } + + var date: Bool + var time: Bool + var holiday: Int + var separator: Separator + + func encode() -> String { + "date: \(date.description), time: \(time.description), holiday: \(holiday.description), separator: \(separator.rawValue)" + } + + init(date: Bool = true, time: Bool = true, holiday: Int = 0, separator: Separator = .space) { + self.date = date + self.time = time + self.holiday = holiday + self.separator = separator + } + + init?(from str: String?) { + guard let str = str else { return nil } + let regex = /([a-zA-Z_0-9]+)\s*:[\s"]*([^\s"#][^"#]*)[\s"#]*(#*.*)$/ + var values = [String: String]() + for line in str.split(separator: ",") { + if let match = try? regex.firstMatch(in: String(line))?.output { + values[String(match.1)] = String(match.2) + } + } + guard let date = values["date"]?.boolValue, let time = values["time"]?.boolValue, let holiday = values["holiday"]?.intValue, let separator = values["separator"] else { return nil } + self.date = date + self.time = time + self.holiday = max(0, min(2, holiday)) + self.separator = Separator(rawValue: separator) ?? .space + } + } + + var textFont = UIFont.systemFont(ofSize: UIFont.systemFontSize, weight: .regular) + var centerFont = UIFont(name: "SourceHanSansKR-Heavy", size: UIFont.systemFontSize)! + + var statusBar = StatusBar() + + private override init() { + super.init() + } + + override func encode(includeOffset: Bool = true, includeColor: Bool = true, includeConfig: Bool = true) -> String { + var encoded = super.encode(includeOffset: includeOffset, includeColor: includeColor, includeConfig: includeConfig) + encoded += "statusBar: \(statusBar.encode())\n" + return encoded + } + + override func update(from values: [String: String], updateSize: Bool = true) { + super.update(from: values, updateSize: updateSize) + if let value = values["statusBar"] { + statusBar = StatusBar(from: value) ?? statusBar + } + } + + var monochrome: Self { + let emptyLayout = Self.init() + emptyLayout.update(from: self.encode(includeColor: false)) + return emptyLayout + } + + func binding(_ keyPath: ReferenceWritableKeyPath) -> Binding { + return Binding(get: { self[keyPath: keyPath] }, set: { self[keyPath: keyPath] = $0 }) + } +} + +@Observable class WatchSetting { + static let shared = WatchSetting() + + var displayTime: Date? = nil + var timezone: TimeZone? = nil + var vertical = true + var settingIsOpen = false + var timeDisplay = "" + + private init() {} + + func binding(_ keyPath: ReferenceWritableKeyPath) -> Binding { + return Binding(get: { self[keyPath: keyPath] }, set: { self[keyPath: keyPath] = $0 }) + } +} diff --git a/Vision/Views/Setting.swift b/Vision/Views/Setting.swift new file mode 100644 index 0000000..a007d06 --- /dev/null +++ b/Vision/Views/Setting.swift @@ -0,0 +1,73 @@ +// +// Settings.swift +// Chinendar +// +// Created by Leo Liu on 11/13/23. +// + +import SwiftUI + +struct Setting: View { + @Environment(\.watchLayout) var watchLayout + @Environment(\.watchSetting) var watchSetting + @Environment(\.modelContext) private var modelContext + + var body: some View { + TabView { + NavigationStack { + Datetime() + } + .tabItem { + Label("日時", systemImage: "clock") + } + NavigationStack { + Location() + } + .tabItem { + Label("經緯度", systemImage: "location") + } + NavigationStack { + RingSetting() + } + .tabItem { + Label("輪色", systemImage: "pencil.and.outline") + } + NavigationStack { + ColorSetting() + } + .tabItem { + Label("色塊", systemImage: "wand.and.stars") + } + NavigationStack { + LayoutSetting() + } + .tabItem { + Label("佈局", systemImage: "square.resize") + } + NavigationStack { + ThemesList() + } + .tabItem { + Label("主題庫", systemImage: "archivebox") + } + NavigationStack { + Documentation() + } + .tabItem { + Label("註釋", systemImage: "doc.questionmark") + } + } + .onDisappear { + watchLayout.saveDefault(context: modelContext) + } + } +} + +#Preview("Settings") { + let watchLayout = WatchLayout.shared + let watchSetting = WatchSetting.shared + + return Setting() + .environment(watchLayout) + .environment(watchSetting) +} diff --git a/Vision/Views/WatchFace.swift b/Vision/Views/WatchFace.swift new file mode 100644 index 0000000..12f9304 --- /dev/null +++ b/Vision/Views/WatchFace.swift @@ -0,0 +1,107 @@ +// +// WatchFace.swift +// Chinendar +// +// Created by Leo Liu on 11/14/23. +// + +import SwiftUI + +@MainActor +struct WatchFace: View { + @Environment(\.chineseCalendar) var chineseCalendar + @Environment(\.watchLayout) var watchLayout + @Environment(\.watchSetting) var watchSetting + @Environment(\.scenePhase) var scenePhase + @Environment(\.modelContext) private var modelContext + @State var showWelcome = false + @State var entityPresenting = EntitySelection() + @State var tapPos: CGPoint? = nil + @State var hoverBounds: CGRect = .zero + @GestureState var longPressed = false + + func tapGesture(proxy: GeometryProxy, size: CGSize) -> some Gesture { + SpatialTapGesture(coordinateSpace: .local) + .onEnded { tap in + tapPos = tap.location + var tapPosition = tap.location + tapPosition.x -= (proxy.size.width - size.width) / 2 + tapPosition.y -= (proxy.size.height - size.height) / 2 + entityPresenting.activeNote = [] + for mark in entityPresenting.entityNotes.entities { + let diff = tapPosition - mark.position + let dist = sqrt(diff.x * diff.x + diff.y * diff.y) + if dist.isFinite && dist < 5 * min(size.width, size.height) * Marks.markSize { + entityPresenting.activeNote.append(mark) + } + } + } + } + + var longPress: some Gesture { + LongPressGesture(minimumDuration: 0.5) + .updating($longPressed) { currentState, gestureState, + transaction in + gestureState = currentState + } + } + + func mainSize(proxy: GeometryProxy) -> CGSize { + var idealSize = watchLayout.watchSize + if proxy.size.height < proxy.size.width { + let width = idealSize.width + idealSize.width = idealSize.height + idealSize.height = width + } + if proxy.size.width * 0.95 < idealSize.width { + let ratio = proxy.size.width * 0.95 / idealSize.width + idealSize.width *= ratio + idealSize.height *= ratio + } + if proxy.size.height * 0.95 < idealSize.height { + let ratio = proxy.size.height * 0.95 / idealSize.height + idealSize.width *= ratio + idealSize.height *= ratio + } + return idealSize + } + + var body: some View { + GeometryReader { proxy in + + let size = watchLayout.watchSize + let centerOffset = if size.height >= size.width { + watchLayout.centerTextOffset + } else { + watchLayout.centerTextHOffset + } + + ZStack { + Watch(displaySubquarter: true, displaySolarTerms: true, compact: false, watchLayout: watchLayout, markSize: 1.0, chineseCalendar: chineseCalendar, widthScale: 0.9, centerOffset: centerOffset, entityNotes: entityPresenting.entityNotes, textShift: true) + .frame(width: size.width, height: size.height) + .position(CGPoint(x: proxy.size.width / 2, y: proxy.size.height / 2)) + .environment(\.scaleEffectScale, longPressed ? -0.1 : 0.0) + .environment(\.scaleEffectAnchor, pressAnchor(pos: tapPos, size: size, proxy: proxy)) + .gesture(longPress) + .simultaneousGesture(tapGesture(proxy: proxy, size: size)) + + Hover(entityPresenting: entityPresenting, bounds: $hoverBounds, tapPos: $tapPos) + } + .onChange(of: proxy.size) { + watchSetting.vertical = proxy.size.height >= proxy.size.width + watchLayout.watchSize = proxy.size + } + .animation(.easeInOut(duration: 0.2), value: entityPresenting.activeNote) + } + .sheet(isPresented: $showWelcome) { + Welcome(size: CGSizeMake(watchLayout.watchSize.width * 0.8, watchLayout.watchSize.height * 0.8)) + } + .task(priority: .background) { + showWelcome = ThemeData.latestVersion() < ThemeData.version + } + } +} + +#Preview("Watch Face") { + WatchFace() +} diff --git a/Vision/Views/Welcome.swift b/Vision/Views/Welcome.swift new file mode 100644 index 0000000..c998d33 --- /dev/null +++ b/Vision/Views/Welcome.swift @@ -0,0 +1,82 @@ +// +// Welcome.swift +// Chinendar +// +// Created by Leo Liu on 11/17/23. +// + +import SwiftUI + +struct Welcome: View { + let size: CGSize + @Environment(\.dismiss) var dismiss + + var body: some View { + let baseLength = min(size.width, size.height) + VStack { + ScrollView { + VStack(spacing: baseLength / 25) { + Spacer(minLength: baseLength / 50) + .frame(maxHeight: baseLength / 25) + Image(.image) + .resizable() + .frame(width: baseLength / 4, height: baseLength / 4) + Text("華曆", comment: "Chinendar") + .font(.largeTitle.bold()) + Spacer(minLength: baseLength / 50) + .frame(maxHeight: baseLength / 25) + VStack(spacing: baseLength / 25) { + HStack { + Image(systemName: "pencil.circle.fill") + .font(.largeTitle) + .frame(width: baseLength / 6, height: baseLength / 6) + .foregroundStyle(Color.accentColor) + VStack(alignment: .leading) { + Text("輪式設計", comment: "Welcome, ring design - title") + .font(.title3) + .padding(.vertical, baseLength / 100) + .padding(.trailing, baseLength / 25) + .frame(maxWidth: .infinity, alignment: .leading) + Text("採用錶盤式設計,不同於以往日曆形制。一年、一月、一日、一時均週而復始,最適以「輪」代表。呈現細節之外,亦不失大局。", comment: "Welcome, ring design - detail") + .font(.body) + } + } + HStack { + Image(systemName: "gearshape.fill") + .font(.largeTitle) + .frame(width: baseLength / 6, height: baseLength / 6) + .foregroundStyle(Color.accentColor) + VStack(alignment: .leading) { + Text("定製自己的專屬風格", comment: "Welcome, setting - title") + .font(.title3) + .padding(.vertical, baseLength / 100) + .padding(.trailing, baseLength / 25) + .frame(maxWidth: .infinity, alignment: .leading) + Text("設置位於錶盤正下方,更改後自動保存。可調時間、在地、外觀等。另有更多有關華曆之介紹。", comment: "Welcome, setting - detail") + .font(.body) + } + } + } + } + } + Spacer(minLength: baseLength / 50) + .frame(maxHeight: baseLength / 25) + + Button { + dismiss() + } label: { + Text("閱", comment: "Ok") + .frame(maxWidth: .infinity) + } + .controlSize(.large) + .buttonStyle(.borderedProminent) + } + .padding() + .frame(width: size.width, height: size.height) + } +} + + +#Preview("Welcome") { + Welcome(size: CGSizeMake(396, 484)) +} diff --git a/Vision/layout.txt b/Vision/layout.txt new file mode 100644 index 0000000..8c14c1d --- /dev/null +++ b/Vision/layout.txt @@ -0,0 +1,41 @@ +globalMonth: false +apparentTime: false +locationEnabled: true +backAlpha: 1.0 +firstRing: locations: 0.0, 0.25, 0.5, 0.75, 0.875; colors: 0xFF95517A, 0xFF9060BC, 0xFF6E68E7, 0xFF00A6FF, 0xFF7556EF; loop: true +secondRing: locations: 0.0, 0.25, 0.5, 0.75; colors: 0xFF8658C5, 0xFF6E68E7, 0xFF5283EF, 0xFF6E68E7; loop: true +thirdRing: locations: 0.0, 0.25, 0.5, 0.75; colors: 0xFF8D5263, 0xFF9252B0, 0xFF7556EF, 0xFF9252B0; loop: true +innerColor: 0xFFFFFFFF +backColor: 0xFFCCCCCC +majorTickColor: 0x00000000 +majorTickAlpha: 0.0 +minorTickColor: 0x00000000 +minorTickAlpha: 0.7 +fontColor: 0xFFFFFFFF +centerFontColor: locations: 0.25, 0.75; colors: 0xFFA36497, 0xFFC28F8A; loop: false +evenSolarTermTickColor: 0xFF000000 +oddSolarTermTickColor: 0xFF555555 +innerColorDark: 0xFF101010 +backColorDark: 0xFF333333 +majorTickColorDark: 0x00000000 +minorTickColorDark: 0x007F7F7F +fontColorDark: 0xFF000000 +evenSolarTermTickColorDark: 0xFFFFFFFF +oddSolarTermTickColorDark: 0xFFAAAAAA +planetIndicator: 0xFF3C1E0A, 0xFFEFF6FF, 0xFF2830D2, 0xFF5AB43C, 0xFF52E9E7, 0xFF5E98DC +eclipseIndicator: 0xFF804427 +fullmoonIndicator: 0xFF3BEFFF +oddStermIndicator: 0xFFAAAAAA +evenStermIndicator: 0xFFFFFFFF +sunPositionIndicator: 0xFF000000, 0xFFAAADFF, 0xFF0A36B1, 0xFF6EBCD3 +moonPositionIndicator: 0xFFE6B8AF, 0xFFE56572, 0xFF9E1E67 +shadeAlpha: 0.25 +shadowSize: 0.03 +centerTextOffset: 0.05 +centerTextHorizontalOffset: 0.05 +verticalTextOffset: 0.0 +horizontalTextOffset: 0.0 +watchWidth: 450.0 +watchHeight: 550.0 +cornerRadiusRatio: 0.5 +statusBar: date: false, time: false, holiday: 1, separator: space diff --git a/Vision/visionApp.swift b/Vision/visionApp.swift new file mode 100644 index 0000000..0991a00 --- /dev/null +++ b/Vision/visionApp.swift @@ -0,0 +1,109 @@ +// +// visionApp.swift +// Chinendar +// +// Created by Leo Liu on 11/5/23. +// + +import SwiftUI + +@main +struct Chinendar: App { + let chineseCalendar = ChineseCalendar(time: .now) + let locationManager = LocationManager.shared + let watchLayout = WatchLayout.shared + let watchSetting = WatchSetting.shared + let timer = Timer.publish(every: ChineseCalendar.updateInterval, on: .main, in: .common).autoconnect() + @Environment(\.openWindow) var openWindow + @Environment(\.dismissWindow) var dismissWindow + private var statusState: StatusState { + StatusState(locationManager: locationManager, watchLayout: watchLayout, watchSetting: watchSetting) + } + + + init() { + let modelContext = ThemeData.container.mainContext + watchLayout.loadDefault(context: modelContext) + locationManager.requestLocation() + } + + var body: some Scene { + WindowGroup(id: "WatchFace") { + WatchFace() + .padding(15) + .environment(\.chineseCalendar, chineseCalendar) + .modelContainer(ThemeData.container) + .task { + self.update() + } + .onReceive(timer) { _ in + self.update() + } + .ornament(attachmentAnchor: .scene(.bottom)) { + HStack(spacing: 0) { + if watchSetting.timeDisplay.count > 0 { + Text(watchSetting.timeDisplay) + .padding() + } + Button { + watchSetting.settingIsOpen.toggle() + } label: { + if watchSetting.timeDisplay.count > 0 { + Label("設置", systemImage: "gear") + .labelStyle(.iconOnly) + } else { + Label("設置", systemImage: "gear") + .labelStyle(.titleAndIcon) + } + } + .onChange(of: watchSetting.settingIsOpen) { + if watchSetting.settingIsOpen { + openWindow(id: "Settings") + } else { + dismissWindow(id: "Settings") + } + } + .buttonStyle(.borderless) + } + .glassBackgroundEffect() + .alignmentGuide(VerticalAlignment.center) { _ in + 15 + } + } + } + .defaultSize(width: watchLayout.watchSize.width + 30, height: watchLayout.watchSize.height + 30) + .windowResizability(.contentSize) + + WindowGroup(id: "Settings") { + Setting() + .environment(\.chineseCalendar, chineseCalendar) + .modelContainer(ThemeData.container) + .onChange(of: statusState) { + watchSetting.timeDisplay = String(statusBar(from: chineseCalendar, options: watchLayout).reversed()) + } + } + .defaultSize(width: 700, height: 700) + } + + func statusBar(from chineseCalendar: ChineseCalendar, options watchLayout: WatchLayout) -> String { + var displayText = [String]() + if watchLayout.statusBar.date { + displayText.append(chineseCalendar.dateString) + } + if watchLayout.statusBar.holiday > 0 { + let holidays = chineseCalendar.holidays + displayText.append(contentsOf: holidays[.. 1 { + watchSetting?.displayTime = newValue + } else { + watchSetting?.displayTime = nil + } chineseCalendar?.update(time: watchSetting?.displayTime ?? .now) } } diff --git a/Watch/Views/Setting.swift b/Watch/Views/Setting.swift index cee8b9c..b8ec784 100644 --- a/Watch/Views/Setting.swift +++ b/Watch/Views/Setting.swift @@ -1,6 +1,6 @@ // // Setting.swift -// Chinese Time Watch +// Chinendar // // Created by Leo Liu on 6/26/23. // diff --git a/Watch/Views/WatchFace.swift b/Watch/Views/WatchFace.swift index 53e2a68..48369a7 100644 --- a/Watch/Views/WatchFace.swift +++ b/Watch/Views/WatchFace.swift @@ -1,6 +1,6 @@ // // WatchFace.swift -// Chinese Time Watch +// Chinendar // // Created by Leo Liu on 6/30/23. // diff --git a/Watch/layout.txt b/Watch/layout.txt index 256995c..d76ab92 100644 --- a/Watch/layout.txt +++ b/Watch/layout.txt @@ -7,6 +7,7 @@ firstRing: locations: 0.0, 0.25, 0.5, 0.75, 0.875; colors: 0xFF95517A, 0xFF9060B secondRing: locations: 0.0, 0.25, 0.5, 0.75; colors: 0xFF8658C5, 0xFF6E68E7, 0xFF5283EF, 0xFF6E68E7; loop: true thirdRing: locations: 0.0, 0.25, 0.5, 0.75; colors: 0xFF8D5263, 0xFF9252B0, 0xFF7556EF, 0xFF9252B0; loop: true innerColor: 0xFFFFFFFF +backColor: 0xFFFFFFFF majorTickColor: 0x00000000 majorTickAlpha: 0.0 minorTickColor: 0x00000000 @@ -16,6 +17,7 @@ centerFontColor: locations: 0.25, 0.75; colors: 0xFFA36497, 0xFFC28F8A; loop: fa evenSolarTermTickColor: 0xFF000000 oddSolarTermTickColor: 0xFF555555 innerColorDark: 0xFF101010 +backColorDark: 0xFF000000 majorTickColorDark: 0x00000000 minorTickColorDark: 0x007F7F7F fontColorDark: 0xFF000000 diff --git a/Watch/watchApp.swift b/Watch/watchApp.swift index a8bddfc..9e867e7 100644 --- a/Watch/watchApp.swift +++ b/Watch/watchApp.swift @@ -1,6 +1,6 @@ // -// ChineseTimeApp.swift -// ChineseTime Watch App +// watchApp.swift +// Chinendar // // Created by Leo Liu on 5/3/23. // @@ -8,7 +8,7 @@ import SwiftUI @main -struct ChineseTimeWatchApp: App { +struct Chinendar: App { let watchConnectivity = WatchConnectivityManager.shared let watchLayout = WatchLayout.shared let watchSetting = WatchSetting.shared diff --git a/Widget/Dual.swift b/Widget/Dual.swift index d0d6eb8..f5a6b41 100644 --- a/Widget/Dual.swift +++ b/Widget/Dual.swift @@ -1,6 +1,6 @@ // // Dual.swift -// Chinese Time +// Chinendar // // Created by Leo Liu on 6/28/23. // diff --git a/Widget/Localizable.xcstrings b/Widget/Localizable.xcstrings index 30c93d4..175e8a6 100644 --- a/Widget/Localizable.xcstrings +++ b/Widget/Localizable.xcstrings @@ -752,7 +752,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Chinese Time" + "value" : "Chinendar" } }, "zh-Hans" : { diff --git a/Widget/Single.swift b/Widget/Single.swift index b718e22..2703e7a 100644 --- a/Widget/Single.swift +++ b/Widget/Single.swift @@ -1,6 +1,6 @@ // // Single.swift -// Chinese Time +// Chinendar // // Created by Leo Liu on 6/28/23. // diff --git a/Widget/TaskGroup.swift b/Widget/TaskGroup.swift index d4c253a..cdf6e50 100644 --- a/Widget/TaskGroup.swift +++ b/Widget/TaskGroup.swift @@ -1,6 +1,6 @@ // // TaskGroup.swift -// Chinese Time Mac +// Chinendar // // Created by Leo Liu on 9/3/23. // diff --git a/Widget/WatchWidgets/Circular.swift b/Widget/WatchWidgets/Circular.swift index 826196a..69d7458 100644 --- a/Widget/WatchWidgets/Circular.swift +++ b/Widget/WatchWidgets/Circular.swift @@ -1,6 +1,6 @@ // // Circular.swift -// Chinese Time +// Chinendar // // Created by Leo Liu on 6/28/23. // diff --git a/Widget/WatchWidgets/CountDown.swift b/Widget/WatchWidgets/CountDown.swift index 10d8cf4..f28ec25 100644 --- a/Widget/WatchWidgets/CountDown.swift +++ b/Widget/WatchWidgets/CountDown.swift @@ -1,6 +1,6 @@ // // CountDown.swift -// Chinese Time +// Chinendar // // Created by Leo Liu on 6/28/23. // diff --git a/Widget/WatchWidgets/Relevance.swift b/Widget/WatchWidgets/Relevance.swift index 19a907e..cdcd1b4 100644 --- a/Widget/WatchWidgets/Relevance.swift +++ b/Widget/WatchWidgets/Relevance.swift @@ -1,6 +1,6 @@ // -// Events.swift -// Chinese Time +// Relevance.swift +// Chinendar // // Created by Leo Liu on 6/28/23. // diff --git a/iOS/Assets.xcassets/AccentColor.colorset/Contents.json b/iOS/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/iOS/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/Chinendar.entitlements b/iOS/Chinendar.entitlements new file mode 100644 index 0000000..ba84f61 --- /dev/null +++ b/iOS/Chinendar.entitlements @@ -0,0 +1,22 @@ + + + + + com.apple.developer.device-information.user-assigned-device-name + + aps-environment + development + com.apple.developer.icloud-container-identifiers + + iCloud.YLiu.ChineseTime + + com.apple.developer.icloud-services + + CloudKit + + com.apple.security.application-groups + + group.ChineseTime + + + diff --git a/iOS/Layout.swift b/iOS/Layout.swift index 1ae3ea3..b71f01b 100644 --- a/iOS/Layout.swift +++ b/iOS/Layout.swift @@ -1,8 +1,8 @@ // // Layout.swift -// ChineseTime +// Chinendar // -// Created by LEO Yoon-Tsaw on 9/23/21. +// Created by Leo Liu on 9/23/21. // import SwiftUI diff --git a/iOS/Views/Setting.swift b/iOS/Views/Setting.swift index 31e3c3e..9c1ea37 100644 --- a/iOS/Views/Setting.swift +++ b/iOS/Views/Setting.swift @@ -1,6 +1,6 @@ // // Setting.swift -// Chinese Time iOS +// Chinendar // // Created by Leo Liu on 6/25/23. // diff --git a/iOS/Views/WatchFace.swift b/iOS/Views/WatchFace.swift index d700877..00d14d3 100644 --- a/iOS/Views/WatchFace.swift +++ b/iOS/Views/WatchFace.swift @@ -1,6 +1,6 @@ // -// WatchFaceView.swift -// Chinese Time iOS +// WatchFace.swift +// Chinendar // // Created by Leo Liu on 6/25/23. // @@ -20,11 +20,13 @@ struct WatchFace: View { @State var tapPos: CGPoint? = nil @State var hoverBounds: CGRect = .zero @GestureState var longPressed = false + var presentSetting: Binding { .init(get: { watchSetting.presentSetting }, set: { newValue in watchSetting.presentSetting = newValue if !newValue { watchLayout.saveDefault(context: modelContext) + WatchConnectivityManager.shared.sendLayout(watchLayout.encode(includeOffset: false)) } }) @@ -100,8 +102,8 @@ struct WatchFace: View { Hover(entityPresenting: entityPresenting, bounds: $hoverBounds, tapPos: $tapPos) } - .onChange(of: proxy.size) { _, newSize in - watchSetting.vertical = newSize.height >= newSize.width + .onChange(of: proxy.size) { + watchSetting.vertical = proxy.size.height >= proxy.size.width } .animation(.easeInOut(duration: 0.2), value: entityPresenting.activeNote) } @@ -116,8 +118,8 @@ struct WatchFace: View { .task(priority: .background) { showWelcome = ThemeData.latestVersion() < ThemeData.version } - .onChange(of: scenePhase) { _, newPhase in - switch newPhase { + .onChange(of: scenePhase) { + switch scenePhase { case .inactive, .background: WatchConnectivityManager.shared.sendLayout(watchLayout.encode(includeOffset: false)) WidgetCenter.shared.reloadAllTimelines() diff --git a/iOS/Views/Welcome.swift b/iOS/Views/Welcome.swift index 48ca148..e5c04af 100644 --- a/iOS/Views/Welcome.swift +++ b/iOS/Views/Welcome.swift @@ -1,6 +1,6 @@ // // Welcome.swift -// Chinese Time iOS +// Chinendar // // Created by Leo Liu on 6/23/23. // @@ -19,7 +19,7 @@ struct Welcome: View { Image(.image) .resizable() .frame(width: 120, height: 120) - Text("華曆", comment: "Chinese Time") + Text("華曆", comment: "Chinendar") .font(.largeTitle.bold()) Spacer(minLength: 10) .frame(maxHeight: 20) diff --git a/iOS/iOSApp.swift b/iOS/iOSApp.swift index 6553e1b..db78886 100644 --- a/iOS/iOSApp.swift +++ b/iOS/iOSApp.swift @@ -1,6 +1,6 @@ // -// AppDelegate.swift -// Chinese Time +// iOSApp.swift +// Chinendar // // Created by Leo Liu on 4/17/23. // @@ -8,7 +8,7 @@ import SwiftUI @main -struct ChineseTimeiOSApp: App { +struct Chinendar: App { let watchConnectivity = WatchConnectivityManager.shared let chineseCalendar = ChineseCalendar(time: .now) let locationManager = LocationManager.shared diff --git a/iOS/layout.txt b/iOS/layout.txt index ccb801f..66da124 100644 --- a/iOS/layout.txt +++ b/iOS/layout.txt @@ -6,6 +6,7 @@ firstRing: locations: 0.0, 0.25, 0.5, 0.75, 0.875; colors: 0xFF95517A, 0xFF9060B secondRing: locations: 0.0, 0.25, 0.5, 0.75; colors: 0xFF8658C5, 0xFF6E68E7, 0xFF5283EF, 0xFF6E68E7; loop: true thirdRing: locations: 0.0, 0.25, 0.5, 0.75; colors: 0xFF8D5263, 0xFF9252B0, 0xFF7556EF, 0xFF9252B0; loop: true innerColor: 0xFFFFFFFF +backColor: 0xFFFFFFFF majorTickColor: 0x00000000 majorTickAlpha: 0.0 minorTickColor: 0x00000000 @@ -15,6 +16,7 @@ centerFontColor: locations: 0.25, 0.75; colors: 0xFFA36497, 0xFFC28F8A; loop: fa evenSolarTermTickColor: 0xFF000000 oddSolarTermTickColor: 0xFF555555 innerColorDark: 0xFF101010 +backColorDark: 0xFF000000 majorTickColorDark: 0x00000000 minorTickColorDark: 0x007F7F7F fontColorDark: 0xFF000000 diff --git a/macOS/ChineseTimeMac.entitlements b/macOS/Chinendar.entitlements similarity index 100% rename from macOS/ChineseTimeMac.entitlements rename to macOS/Chinendar.entitlements diff --git a/macOS/Layout.swift b/macOS/Layout.swift index 9c4a33a..bc4220b 100644 --- a/macOS/Layout.swift +++ b/macOS/Layout.swift @@ -1,8 +1,8 @@ // // Layout.swift -// ChineseTime +// Chinendar // -// Created by LEO Yoon-Tsaw on 9/23/21. +// Created by Leo Liu on 9/23/21. // import SwiftUI @@ -74,8 +74,8 @@ import Observation return encoded } - override func update(from values: [String: String]) { - super.update(from: values) + override func update(from values: [String: String], updateSize: Bool = true) { + super.update(from: values, updateSize: updateSize) if let name = values["textFont"] { textFont = NSFont(name: name, size: NSFont.systemFontSize) ?? textFont } diff --git a/macOS/Views/Setting.swift b/macOS/Views/Setting.swift index f388e56..6e372b9 100644 --- a/macOS/Views/Setting.swift +++ b/macOS/Views/Setting.swift @@ -1,6 +1,6 @@ // -// SwiftUIView.swift -// Chinese Time +// Setting.swift +// Chinendar // // Created by Leo Liu on 6/30/23. // @@ -11,10 +11,16 @@ import WidgetKit struct Setting: View { @Environment(\.watchLayout) var watchLayout @Environment(\.watchSetting) var watchSetting + @Environment(\.chineseCalendar) var chineseCalendar + @Environment(\.locationManager) var locationManager @State private var selection: WatchSetting.Selection? = .none @State private var columnVisibility = NavigationSplitViewVisibility.automatic @Environment(\.modelContext) private var modelContext + private var statusState: StatusState { + StatusState(locationManager: locationManager, watchLayout: watchLayout, watchSetting: watchSetting) + } + var body: some View { NavigationSplitView(columnVisibility: $columnVisibility) { List(selection: $selection) { @@ -69,9 +75,14 @@ struct Setting: View { } } .navigationSplitViewStyle(.balanced) - .onChange(of: selection) { _, _ in + .onChange(of: selection) { cleanColorPanel() } + .onChange(of: statusState) { + if let delegate = AppDelegate.instance { + delegate.updateStatusBar(dateText: delegate.statusBar(from: chineseCalendar, options: watchLayout)) + } + } .onAppear { selection = watchSetting.previousSelection ?? .datetime } @@ -88,47 +99,19 @@ struct Setting: View { func buildView(selection: WatchSetting.Selection) -> some View { let sel = switch selection { case .datetime: - Label { - Text("日時", comment: "Display time settings") - } icon: { - Image(systemName: "clock") - } + Label("日時", systemImage: "clock") case .location: - Label { - Text("經緯度", comment: "Geo Location section") - } icon: { - Image(systemName: "location") - } + Label("經緯度", systemImage: "location") case .ringColor: - Label { - Text("輪色", comment: "Rings Color Setting") - } icon: { - Image(systemName: "pencil.and.outline") - } + Label("輪色", systemImage: "pencil.and.outline") case .markColor: - Label { - Text("色塊", comment: "Mark Color settings") - } icon: { - Image(systemName: "wand.and.stars") - } + Label("色塊", systemImage: "wand.and.stars") case .layout: - Label { - Text("佈局", comment: "Layout settings section") - } icon: { - Image(systemName: "square.resize") - } + Label("佈局", systemImage: "square.resize") case .themes: - Label { - Text("主題庫", comment: "manage saved themes") - } icon: { - Image(systemName: "archivebox") - } + Label("主題庫", systemImage: "archivebox") case .documentation: - Label { - Text("註釋", comment: "Documentation View") - } icon: { - Image(systemName: "doc.questionmark") - } + Label("註釋", systemImage: "doc.questionmark") } return sel } diff --git a/macOS/Views/WatchFace.swift b/macOS/Views/WatchFace.swift index a7df342..2f17841 100644 --- a/macOS/Views/WatchFace.swift +++ b/macOS/Views/WatchFace.swift @@ -1,6 +1,6 @@ // // WatchFace.swift -// Chinese Time mac +// Chinendar // // Created by Leo Liu on 7/1/23. // diff --git a/macOS/Views/Welcome.swift b/macOS/Views/Welcome.swift index f49624e..2035662 100644 --- a/macOS/Views/Welcome.swift +++ b/macOS/Views/Welcome.swift @@ -1,6 +1,6 @@ // // Welcome.swift -// Chinese Time mac +// Chinendar // // Created by Leo Liu on 8/4/23. // @@ -15,7 +15,7 @@ struct Welcome: View { Image(.image) .resizable() .frame(width: 120, height: 120) - Text("華曆", comment: "Chinese Time") + Text("華曆", comment: "Chinendar") .font(.largeTitle.bold()) HStack { Image(systemName: "menubar.rectangle") diff --git a/macOS/layout.txt b/macOS/layout.txt index ebad847..23f0916 100644 --- a/macOS/layout.txt +++ b/macOS/layout.txt @@ -6,6 +6,7 @@ firstRing: locations: 0.0, 0.25, 0.5, 0.75, 0.875; colors: 0xFF95517A, 0xFF9060B secondRing: locations: 0.0, 0.25, 0.5, 0.75; colors: 0xFF8658C5, 0xFF6E68E7, 0xFF5283EF, 0xFF6E68E7; loop: true thirdRing: locations: 0.0, 0.25, 0.5, 0.75; colors: 0xFF8D5263, 0xFF9252B0, 0xFF7556EF, 0xFF9252B0; loop: true innerColor: 0xFFFFFFFF +backColor: 0xFFFFFFFF majorTickColor: 0x00000000 majorTickAlpha: 0.0 minorTickColor: 0x00000000 @@ -15,6 +16,7 @@ centerFontColor: locations: 0.25, 0.75; colors: 0xFFA36497, 0xFFC28F8A; loop: fa evenSolarTermTickColor: 0xFF000000 oddSolarTermTickColor: 0xFF555555 innerColorDark: 0xFF101010 +backColorDark: 0xFF303030 majorTickColorDark: 0x00000000 minorTickColorDark: 0x007F7F7F fontColorDark: 0xFF000000 diff --git a/macOS/macApp.swift b/macOS/macApp.swift index d465d61..2192fd0 100644 --- a/macOS/macApp.swift +++ b/macOS/macApp.swift @@ -1,15 +1,15 @@ // -// AppDelegate.swift -// ChineseTime +// macApp.swift +// Chinendar // -// Created by LEO Yoon-Tsaw on 9/19/21. +// Created by Leo Liu on 9/19/21. // import SwiftUI import WidgetKit @main -struct ChineseTimeMacApp: App { +struct Chinendar: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { diff --git a/macOS/watchPanel.swift b/macOS/watchPanel.swift index b102570..416fc74 100644 --- a/macOS/watchPanel.swift +++ b/macOS/watchPanel.swift @@ -1,6 +1,6 @@ // -// watchPanel.swift -// Chinese Time mac +// WatchPanel.swift +// Chinendar // // Created by Leo Liu on 8/1/23. //