diff --git a/Assets/Resources/SHI/Images/playbar_bg.png b/Assets/Resources/SHI/Images/playbar_bg.png
new file mode 100644
index 00000000..660439ca
Binary files /dev/null and b/Assets/Resources/SHI/Images/playbar_bg.png differ
diff --git a/Assets/Resources/SHI/Images/playbar_bg.png.meta b/Assets/Resources/SHI/Images/playbar_bg.png.meta
new file mode 100644
index 00000000..6472e227
--- /dev/null
+++ b/Assets/Resources/SHI/Images/playbar_bg.png.meta
@@ -0,0 +1,143 @@
+fileFormatVersion: 2
+guid: f019cda87b206f84098dd38575768a72
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 13
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ flipGreenChannel: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ ignoreMipmapLimit: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 1
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 8
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ swizzle: 50462976
+ cookieLightType: 0
+ platformSettings:
+ - serializedVersion: 4
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 256
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: WebGL
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: WindowsStoreApps
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ customData:
+ physicsShape: []
+ bones: []
+ spriteID: 5e97eb03825dee720800000000000000
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spriteCustomMetadata:
+ entries: []
+ nameFileIdTable: {}
+ mipmapLimitGroupName:
+ pSDRemoveMatte: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/SHI/Images/playbar_icon_end_16x21.png b/Assets/Resources/SHI/Images/playbar_icon_end_16x21.png
new file mode 100644
index 00000000..7aa284b9
Binary files /dev/null and b/Assets/Resources/SHI/Images/playbar_icon_end_16x21.png differ
diff --git a/Assets/Resources/SHI/Images/playbar_icon_end_16x21.png.meta b/Assets/Resources/SHI/Images/playbar_icon_end_16x21.png.meta
new file mode 100644
index 00000000..3f0ffef1
--- /dev/null
+++ b/Assets/Resources/SHI/Images/playbar_icon_end_16x21.png.meta
@@ -0,0 +1,143 @@
+fileFormatVersion: 2
+guid: 25aaebf107892914cada8d94caeef0b2
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 13
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ flipGreenChannel: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ ignoreMipmapLimit: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 1
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 8
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ swizzle: 50462976
+ cookieLightType: 0
+ platformSettings:
+ - serializedVersion: 4
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 32
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: WebGL
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: WindowsStoreApps
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ customData:
+ physicsShape: []
+ bones: []
+ spriteID: 5e97eb03825dee720800000000000000
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spriteCustomMetadata:
+ entries: []
+ nameFileIdTable: {}
+ mipmapLimitGroupName:
+ pSDRemoveMatte: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/SHI/Images/playbar_icon_pause_17.png b/Assets/Resources/SHI/Images/playbar_icon_pause_17.png
new file mode 100644
index 00000000..f4057938
Binary files /dev/null and b/Assets/Resources/SHI/Images/playbar_icon_pause_17.png differ
diff --git a/Assets/Resources/SHI/Images/playbar_icon_pause_17.png.meta b/Assets/Resources/SHI/Images/playbar_icon_pause_17.png.meta
new file mode 100644
index 00000000..4be17294
--- /dev/null
+++ b/Assets/Resources/SHI/Images/playbar_icon_pause_17.png.meta
@@ -0,0 +1,143 @@
+fileFormatVersion: 2
+guid: 9e72c11031ee853409b2890dfe74d9cb
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 13
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ flipGreenChannel: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ ignoreMipmapLimit: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 1
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 8
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ swizzle: 50462976
+ cookieLightType: 0
+ platformSettings:
+ - serializedVersion: 4
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 32
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: WebGL
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: WindowsStoreApps
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ customData:
+ physicsShape: []
+ bones: []
+ spriteID: 5e97eb03825dee720800000000000000
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spriteCustomMetadata:
+ entries: []
+ nameFileIdTable: {}
+ mipmapLimitGroupName:
+ pSDRemoveMatte: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/SHI/Images/playbar_icon_play_17x21.png b/Assets/Resources/SHI/Images/playbar_icon_play_17x21.png
new file mode 100644
index 00000000..eb2dc23d
Binary files /dev/null and b/Assets/Resources/SHI/Images/playbar_icon_play_17x21.png differ
diff --git a/Assets/Resources/SHI/Images/playbar_icon_play_17x21.png.meta b/Assets/Resources/SHI/Images/playbar_icon_play_17x21.png.meta
new file mode 100644
index 00000000..2ed6f038
--- /dev/null
+++ b/Assets/Resources/SHI/Images/playbar_icon_play_17x21.png.meta
@@ -0,0 +1,143 @@
+fileFormatVersion: 2
+guid: 989509807ae628144bf9ec45a2dc4397
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 13
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ flipGreenChannel: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ ignoreMipmapLimit: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 1
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 8
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ swizzle: 50462976
+ cookieLightType: 0
+ platformSettings:
+ - serializedVersion: 4
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 32
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: WebGL
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: WindowsStoreApps
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ customData:
+ physicsShape: []
+ bones: []
+ spriteID: 5e97eb03825dee720800000000000000
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spriteCustomMetadata:
+ entries: []
+ nameFileIdTable: {}
+ mipmapLimitGroupName:
+ pSDRemoveMatte: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/SHI/Images/playbar_icon_start_16x21.png b/Assets/Resources/SHI/Images/playbar_icon_start_16x21.png
new file mode 100644
index 00000000..40e7c4ab
Binary files /dev/null and b/Assets/Resources/SHI/Images/playbar_icon_start_16x21.png differ
diff --git a/Assets/Resources/SHI/Images/playbar_icon_start_16x21.png.meta b/Assets/Resources/SHI/Images/playbar_icon_start_16x21.png.meta
new file mode 100644
index 00000000..98c299e9
--- /dev/null
+++ b/Assets/Resources/SHI/Images/playbar_icon_start_16x21.png.meta
@@ -0,0 +1,143 @@
+fileFormatVersion: 2
+guid: 67a53c30e0da69e4e899c7dddfaff16e
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 13
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ flipGreenChannel: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ ignoreMipmapLimit: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 1
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 8
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ swizzle: 50462976
+ cookieLightType: 0
+ platformSettings:
+ - serializedVersion: 4
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 32
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: WebGL
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: WindowsStoreApps
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ customData:
+ physicsShape: []
+ bones: []
+ spriteID: 5e97eb03825dee720800000000000000
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spriteCustomMetadata:
+ entries: []
+ nameFileIdTable: {}
+ mipmapLimitGroupName:
+ pSDRemoveMatte: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/SHI/Images/playbar_icon_stop_17.png b/Assets/Resources/SHI/Images/playbar_icon_stop_17.png
new file mode 100644
index 00000000..ec693b99
Binary files /dev/null and b/Assets/Resources/SHI/Images/playbar_icon_stop_17.png differ
diff --git a/Assets/Resources/SHI/Images/playbar_icon_stop_17.png.meta b/Assets/Resources/SHI/Images/playbar_icon_stop_17.png.meta
new file mode 100644
index 00000000..b5ecb02e
--- /dev/null
+++ b/Assets/Resources/SHI/Images/playbar_icon_stop_17.png.meta
@@ -0,0 +1,143 @@
+fileFormatVersion: 2
+guid: 5a70228c7e2d0fa4fb56924dca7cf261
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 13
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ flipGreenChannel: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ ignoreMipmapLimit: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 1
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 8
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ swizzle: 50462976
+ cookieLightType: 0
+ platformSettings:
+ - serializedVersion: 4
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 32
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: WebGL
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: WindowsStoreApps
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ customData:
+ physicsShape: []
+ bones: []
+ spriteID: 5e97eb03825dee720800000000000000
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spriteCustomMetadata:
+ entries: []
+ nameFileIdTable: {}
+ mipmapLimitGroupName:
+ pSDRemoveMatte: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/SHI/Modal/ISOP/ISOPModal.uss b/Assets/Resources/SHI/Modal/ISOP/ISOPModal.uss
index 6b12434c..99920dcc 100644
--- a/Assets/Resources/SHI/Modal/ISOP/ISOPModal.uss
+++ b/Assets/Resources/SHI/Modal/ISOP/ISOPModal.uss
@@ -1,68 +1,34 @@
-/*
- * ISOPModal.uss - ISOP 모달 전역 스타일시트
- *
- * [개요]
- * ISOP 모달 창 전체에 적용되는 스타일을 정의합니다.
- * 주로 스크롤바 커스터마이징과 기본 텍스트 스타일을 포함합니다.
- *
- * [스타일 구조]
- * 1. .unity-text-element : 기본 텍스트 스타일 (Pretendard 폰트, 10px)
- * 2. .unity-scroller--vertical : 세로 스크롤바 커스터마이징
- * 3. .unity-scroller--horizontal : 가로 스크롤바 커스터마이징
- *
- * [스크롤바 디자인]
- * - 너비/높이: 6px (슬림한 디자인)
- * - 트랙: 흰색 배경 (#FFFFFF)
- * - 드래거: 밝은 회색 (#D8D8D8), 둥근 모서리 (3px)
- * - 화살표 버튼: 숨김 처리
- *
- * [폰트 설정]
- * - 기본 폰트: Pretendard-Regular
- * - 기본 색상: rgb(34, 34, 34) - 진한 회색
- * - 기본 크기: 10px
- *
- * [연관 파일]
- * - ISOPModal.uxml : 메인 레이아웃
- * - ISOPChart.uss : 간트 차트 스타일
- * - ISOPModelView.uss : 모델 뷰 스타일
- */
-
-/* ===================================
- 기본 텍스트 스타일
- 모달 내 모든 텍스트 요소에 적용
- =================================== */
.unity-text-element {
-unity-font: url('project://database/Assets/Fonts/Pretendard-Regular.otf');
-unity-font-definition: resource('Fonts/Pretendard/Pretendard-Regular');
- color: rgb(34, 34, 34); /* 진한 회색 텍스트 */
+ color: rgb(34, 34, 34);
font-size: 10px;
}
-/* ===================================
- 세로 스크롤바 스타일
- 슬림한 6px 너비의 커스텀 스크롤바
- =================================== */
.unity-scroller--vertical {
- width: 6px; /* 슬림한 너비 */
- margin-right: 4px;
- margin-bottom: 0px;
-}
-
-/* 세로 스크롤바 트랙 (배경) */
-.unity-scroller--vertical .unity-base-slider__tracker {
- background-color: rgb(255, 255, 255); /* 흰색 배경 */
- border-width: 0;
-}
-
-/* 세로 스크롤바 드래거 (핸들) */
-.unity-scroller--vertical .unity-base-slider__dragger {
- background-color: rgb(216, 216, 216); /* 밝은 회색 */
- border-width: 0;
- border-radius: 3px; /* 둥근 모서리 */
width: 6px;
+ margin-right: 4px;
+ margin-bottom: 0;
+}
+
+.unity-scroller--vertical .unity-base-slider__tracker {
+ background-color: rgb(255, 255, 255);
+ border-width: 0;
+}
+
+.unity-scroller--vertical .unity-base-slider__drag-container {
+ left: 0;
+ right: 0;
+}
+
+.unity-scroller--vertical .unity-base-slider__dragger {
+ background-color: rgb(216, 216, 216);
+ border-width: 0;
+ border-radius: 3px;
+ width: 6px;
+ left: 0;
}
-/* 세로 스크롤바 화살표 버튼 숨김 */
.unity-scroller--vertical .unity-repeat-button {
display: none;
width: 0;
@@ -71,36 +37,39 @@
min-height: 0;
}
-/* 세로 스크롤바 슬라이더 마진 제거 */
.unity-scroller--vertical .unity-slider {
margin: 0;
}
-/* ===================================
- 가로 스크롤바 스타일
- 슬림한 6px 높이의 커스텀 스크롤바
- =================================== */
+.unity-scroller--vertical .unity-base-field__input {
+ width: 6px;
+ min-width: 6px;
+}
+
.unity-scroller--horizontal {
- height: 6px; /* 슬림한 높이 */
- margin-bottom: 4px;
- margin-right: 0px;
-}
-
-/* 가로 스크롤바 트랙 (배경) */
-.unity-scroller--horizontal .unity-base-slider__tracker {
- background-color: rgb(255, 255, 255); /* 흰색 배경 */
- border-width: 0;
-}
-
-/* 가로 스크롤바 드래거 (핸들) */
-.unity-scroller--horizontal .unity-base-slider__dragger {
- background-color: rgb(216, 216, 216); /* 밝은 회색 */
- border-width: 0;
- border-radius: 3px; /* 둥근 모서리 */
height: 6px;
+ margin-bottom: 4px;
+ margin-right: 0;
+}
+
+.unity-scroller--horizontal .unity-base-slider__tracker {
+ background-color: rgb(255, 255, 255);
+ border-width: 0;
+}
+
+.unity-scroller--horizontal .unity-base-slider__drag-container {
+ top: 0;
+ bottom: 0;
+}
+
+.unity-scroller--horizontal .unity-base-slider__dragger {
+ background-color: rgb(216, 216, 216);
+ border-width: 0;
+ border-radius: 3px;
+ height: 6px;
+ top: 0;
}
-/* 가로 스크롤바 화살표 버튼 숨김 */
.unity-scroller--horizontal .unity-repeat-button {
display: none;
width: 0;
@@ -109,7 +78,48 @@
min-height: 0;
}
-/* 가로 스크롤바 슬라이더 마진 제거 */
.unity-scroller--horizontal .unity-slider {
margin: 0;
}
+
+.unity-scroller--horizontal .unity-base-field__input {
+ height: 6px;
+ min-height: 6px;
+}
+
+.loading-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ justify-content: center;
+ align-items: center;
+ display: none;
+}
+
+.loading-overlay.visible {
+ display: flex;
+}
+
+.loading-spinner {
+ width: 24px;
+ height: 24px;
+ border-top-width: 4px;
+ border-right-width: 4px;
+ border-bottom-width: 4px;
+ border-left-width: 4px;
+ border-top-color: rgba(255, 255, 255, 0.3);
+ border-right-color: rgba(255, 255, 255, 0.3);
+ border-bottom-color: rgba(255, 255, 255, 0.3);
+ border-left-color: rgb(255, 255, 255);
+ border-top-left-radius: 50%;
+ border-top-right-radius: 50%;
+ border-bottom-left-radius: 50%;
+ border-bottom-right-radius: 50%;
+ align-self: center;
+ align-content: center;
+ justify-content: center;
+ align-items: center;
+}
diff --git a/Assets/Resources/SHI/Modal/ISOP/ISOPModal.uxml b/Assets/Resources/SHI/Modal/ISOP/ISOPModal.uxml
index 01437808..0f7017b2 100644
--- a/Assets/Resources/SHI/Modal/ISOP/ISOPModal.uxml
+++ b/Assets/Resources/SHI/Modal/ISOP/ISOPModal.uxml
@@ -1,72 +1,17 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
diff --git a/Assets/Resources/SHI/Modal/Modal.uss b/Assets/Resources/SHI/Modal/Modal.uss
new file mode 100644
index 00000000..d6ac31f6
--- /dev/null
+++ b/Assets/Resources/SHI/Modal/Modal.uss
@@ -0,0 +1,13 @@
+
+/* ===================================
+ 드롭다운 팝업 아이템 스타일
+ =================================== */
+.unity-base-dropdown__item {
+ padding: 0px;
+ background-color: rgb(40, 44, 52);
+ color: rgb(255, 255, 255);
+}
+
+.unity-base-dropdown__container-inner {
+ padding: 0px;
+}
\ No newline at end of file
diff --git a/Assets/Resources/SHI/Modal/Modal.uss.meta b/Assets/Resources/SHI/Modal/Modal.uss.meta
new file mode 100644
index 00000000..1389105d
--- /dev/null
+++ b/Assets/Resources/SHI/Modal/Modal.uss.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 15f8f9b790a1042468c972e5ea4e9430
+ScriptedImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 2
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+ script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
+ disableValidation: 0
diff --git a/Assets/Resources/SHI/Modal/NW/NWModal.uss b/Assets/Resources/SHI/Modal/NW/NWModal.uss
index 81360e9c..990c983e 100644
--- a/Assets/Resources/SHI/Modal/NW/NWModal.uss
+++ b/Assets/Resources/SHI/Modal/NW/NWModal.uss
@@ -1,69 +1,34 @@
-/*
- * NWModal.uss - NW 모달 전역 스타일시트
- *
- * [개요]
- * NW 모달 창 전체에 적용되는 스타일을 정의합니다.
- * ISOPModal.uss와 동일한 스타일을 사용합니다.
- * 주로 스크롤바 커스터마이징과 기본 텍스트 스타일을 포함합니다.
- *
- * [스타일 구조]
- * 1. .unity-text-element : 기본 텍스트 스타일 (Pretendard 폰트, 10px)
- * 2. .unity-scroller--vertical : 세로 스크롤바 커스터마이징
- * 3. .unity-scroller--horizontal : 가로 스크롤바 커스터마이징
- *
- * [스크롤바 디자인]
- * - 너비/높이: 6px (슬림한 디자인)
- * - 트랙: 흰색 배경 (#FFFFFF)
- * - 드래거: 밝은 회색 (#D8D8D8), 둥근 모서리 (3px)
- * - 화살표 버튼: 숨김 처리
- *
- * [폰트 설정]
- * - 기본 폰트: Pretendard-Regular
- * - 기본 색상: rgb(34, 34, 34) - 진한 회색
- * - 기본 크기: 10px
- *
- * [연관 파일]
- * - NWModal.uxml : 메인 레이아웃
- * - NWChart.uss : 네트워크 다이어그램 스타일
- * - NWModalView.uss : 모델 뷰 스타일
- */
-
-/* ===================================
- 기본 텍스트 스타일
- 모달 내 모든 텍스트 요소에 적용
- =================================== */
.unity-text-element {
-unity-font: url('project://database/Assets/Fonts/Pretendard-Regular.otf');
-unity-font-definition: resource('Fonts/Pretendard/Pretendard-Regular');
- color: rgb(34, 34, 34); /* 진한 회색 텍스트 */
+ color: rgb(34, 34, 34);
font-size: 10px;
}
-/* ===================================
- 세로 스크롤바 스타일
- 슬림한 6px 너비의 커스텀 스크롤바
- =================================== */
.unity-scroller--vertical {
- width: 6px; /* 슬림한 너비 */
- margin-right: 4px;
- margin-bottom: 0px;
-}
-
-/* 세로 스크롤바 트랙 (배경) */
-.unity-scroller--vertical .unity-base-slider__tracker {
- background-color: rgb(255, 255, 255); /* 흰색 배경 */
- border-width: 0;
-}
-
-/* 세로 스크롤바 드래거 (핸들) */
-.unity-scroller--vertical .unity-base-slider__dragger {
- background-color: rgb(216, 216, 216); /* 밝은 회색 */
- border-width: 0;
- border-radius: 3px; /* 둥근 모서리 */
width: 6px;
+ margin-right: 4px;
+ margin-bottom: 0;
+}
+
+.unity-scroller--vertical .unity-base-slider__tracker {
+ background-color: rgb(255, 255, 255);
+ border-width: 0;
+}
+
+.unity-scroller--vertical .unity-base-slider__drag-container {
+ left: 0;
+ right: 0;
+}
+
+.unity-scroller--vertical .unity-base-slider__dragger {
+ background-color: rgb(216, 216, 216);
+ border-width: 0;
+ border-radius: 3px;
+ width: 6px;
+ left: 0;
}
-/* 세로 스크롤바 화살표 버튼 숨김 */
.unity-scroller--vertical .unity-repeat-button {
display: none;
width: 0;
@@ -72,36 +37,39 @@
min-height: 0;
}
-/* 세로 스크롤바 슬라이더 마진 제거 */
.unity-scroller--vertical .unity-slider {
margin: 0;
}
-/* ===================================
- 가로 스크롤바 스타일
- 슬림한 6px 높이의 커스텀 스크롤바
- =================================== */
+.unity-scroller--vertical .unity-base-field__input {
+ width: 6px;
+ min-width: 6px;
+}
+
.unity-scroller--horizontal {
- height: 6px; /* 슬림한 높이 */
- margin-bottom: 4px;
- margin-right: 0px;
-}
-
-/* 가로 스크롤바 트랙 (배경) */
-.unity-scroller--horizontal .unity-base-slider__tracker {
- background-color: rgb(255, 255, 255); /* 흰색 배경 */
- border-width: 0;
-}
-
-/* 가로 스크롤바 드래거 (핸들) */
-.unity-scroller--horizontal .unity-base-slider__dragger {
- background-color: rgb(216, 216, 216); /* 밝은 회색 */
- border-width: 0;
- border-radius: 3px; /* 둥근 모서리 */
height: 6px;
+ margin-bottom: 4px;
+ margin-right: 0;
+}
+
+.unity-scroller--horizontal .unity-base-slider__tracker {
+ background-color: rgb(255, 255, 255);
+ border-width: 0;
+}
+
+.unity-scroller--horizontal .unity-base-slider__drag-container {
+ top: 0;
+ bottom: 0;
+}
+
+.unity-scroller--horizontal .unity-base-slider__dragger {
+ background-color: rgb(216, 216, 216);
+ border-width: 0;
+ border-radius: 3px;
+ height: 6px;
+ top: 0;
}
-/* 가로 스크롤바 화살표 버튼 숨김 */
.unity-scroller--horizontal .unity-repeat-button {
display: none;
width: 0;
@@ -110,7 +78,48 @@
min-height: 0;
}
-/* 가로 스크롤바 슬라이더 마진 제거 */
.unity-scroller--horizontal .unity-slider {
margin: 0;
}
+
+.unity-scroller--horizontal .unity-base-field__input {
+ height: 6px;
+ min-height: 6px;
+}
+
+.loading-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ justify-content: center;
+ align-items: center;
+ display: none;
+}
+
+.loading-overlay.visible {
+ display: flex;
+}
+
+.loading-spinner {
+ width: 24px;
+ height: 24px;
+ border-top-width: 4px;
+ border-right-width: 4px;
+ border-bottom-width: 4px;
+ border-left-width: 4px;
+ border-top-color: rgba(255, 255, 255, 0.3);
+ border-right-color: rgba(255, 255, 255, 0.3);
+ border-bottom-color: rgba(255, 255, 255, 0.3);
+ border-left-color: rgb(255, 255, 255);
+ border-top-left-radius: 50%;
+ border-top-right-radius: 50%;
+ border-bottom-left-radius: 50%;
+ border-bottom-right-radius: 50%;
+ align-self: center;
+ align-items: center;
+ justify-content: center;
+ align-content: center;
+}
diff --git a/Assets/Resources/SHI/Modal/NW/NWModal.uxml b/Assets/Resources/SHI/Modal/NW/NWModal.uxml
index b261270f..9a061c06 100644
--- a/Assets/Resources/SHI/Modal/NW/NWModal.uxml
+++ b/Assets/Resources/SHI/Modal/NW/NWModal.uxml
@@ -1,76 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/Assets/Resources/SHI/Modal/NW/NWModelView.uxml b/Assets/Resources/SHI/Modal/NW/NWModelView.uxml
index 877d1d1a..3c5991aa 100644
--- a/Assets/Resources/SHI/Modal/NW/NWModelView.uxml
+++ b/Assets/Resources/SHI/Modal/NW/NWModelView.uxml
@@ -40,7 +40,7 @@
-
+
diff --git a/Assets/Resources/SHI/Modal/PlayBar.uss b/Assets/Resources/SHI/Modal/PlayBar.uss
new file mode 100644
index 00000000..fc873ac0
--- /dev/null
+++ b/Assets/Resources/SHI/Modal/PlayBar.uss
@@ -0,0 +1,152 @@
+.playbar-container {
+ background-color: rgba(255, 255, 255, 0);
+ padding-top: 15px;
+ padding-right: 20px;
+ padding-bottom: 15px;
+ padding-left: 20px;
+ flex-direction: column;
+ width: 100%;
+ height: 130px;
+ background-image: resource('SHI/Images/playbar_bg');
+}
+
+.progress-section {
+ flex-direction: row;
+ align-items: center;
+ margin-bottom: 10px;
+}
+
+.date-label {
+ color: rgb(255, 255, 255);
+ font-size: 14px;
+ -unity-font-definition: resource('Fonts/Pretendard/Pretendard-Regular');
+ min-width: 70px;
+}
+
+.start-label {
+ -unity-text-align: middle-left;
+ margin-right: 10px;
+}
+
+.end-label {
+ -unity-text-align: middle-right;
+ margin-left: 10px;
+}
+
+.progress-track {
+ flex-grow: 1;
+ height: 4px;
+ background-color: rgba(0, 0, 0, 0.3);
+ position: relative;
+}
+
+.progress-fill {
+ position: absolute;
+ left: 0;
+ top: 0;
+ height: 100%;
+ background-color: rgb(0, 70, 140);
+ width: 50%;
+}
+
+.controls-section {
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.button-group {
+ flex-direction: row;
+ align-items: center;
+}
+
+.control-btn {
+ width: 24px;
+ height: 24px;
+ margin-top: 0;
+ margin-right: 4px;
+ margin-bottom: 0;
+ margin-left: 4px;
+ padding-top: 0;
+ padding-right: 0;
+ padding-bottom: 0;
+ padding-left: 0;
+ border-top-width: 0;
+ border-right-width: 0;
+ border-bottom-width: 0;
+ border-left-width: 0;
+ background-color: rgba(0, 0, 0, 0);
+ -unity-background-scale-mode: scale-to-fit;
+}
+
+.control-btn:hover {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+.control-btn:active {
+ background-color: rgba(0, 0, 0, 0.2);
+}
+
+.play-btn {
+ background-image: resource('SHI/Images/playbar_icon_play_17x21');
+}
+
+.play-btn.playing {
+ background-image: resource('SHI/Images/playbar_icon_pause_17');
+}
+
+.stop-btn {
+ background-image: resource('SHI/Images/playbar_icon_stop_17');
+}
+
+.first-btn {
+ background-image: resource('SHI/Images/playbar_icon_start_16x21');
+}
+
+.last-btn {
+ background-image: resource('SHI/Images/playbar_icon_end_16x21');
+}
+
+.current-time-label {
+ color: rgb(255, 255, 255);
+ font-size: 14px;
+ -unity-font-definition: resource('Fonts/Pretendard/Pretendard-SemiBold');
+ -unity-text-align: middle-center;
+}
+
+.interval-dropdown {
+ width: 60px;
+ height: 24px;
+ background-color: rgb(255, 255, 255);
+ border-radius: 4px;
+ border-top-width: 1px;
+ border-right-width: 1px;
+ border-bottom-width: 1px;
+ border-left-width: 1px;
+ border-color: rgb(180, 180, 180);
+ color: rgb(50, 50, 50);
+ font-size: 12px;
+ -unity-font-definition: resource('Fonts/Pretendard/Pretendard-Regular');
+}
+
+.interval-dropdown .unity-base-popup-field__input {
+ background-color: rgb(255, 255, 255);
+ border-width: 0;
+ padding-left: 4px;
+ padding-top: 0;
+ padding-right: 8px;
+ padding-bottom: 0;
+}
+
+.interval-dropdown .unity-base-popup-field__text {
+ color: rgb(50, 50, 50);
+ -unity-text-align: middle-left;
+ margin-top: 0;
+ margin-right: 0;
+ margin-bottom: 0;
+ margin-left: 0;
+}
+
+.interval-dropdown .unity-base-popup-field__arrow {
+ -unity-background-image-tint-color: rgb(100, 100, 100);
+}
diff --git a/Assets/Resources/SHI/Modal/PlayBar.uss.meta b/Assets/Resources/SHI/Modal/PlayBar.uss.meta
new file mode 100644
index 00000000..300ca34c
--- /dev/null
+++ b/Assets/Resources/SHI/Modal/PlayBar.uss.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 21091244fd5f49d42a6694b75fe2c395
+ScriptedImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 2
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+ script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
+ disableValidation: 0
diff --git a/Assets/Resources/SHI/Modal/PlayBar.uxml b/Assets/Resources/SHI/Modal/PlayBar.uxml
new file mode 100644
index 00000000..bf73dc0a
--- /dev/null
+++ b/Assets/Resources/SHI/Modal/PlayBar.uxml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Assets/Resources/SHI/Modal/PlayBar.uxml.meta b/Assets/Resources/SHI/Modal/PlayBar.uxml.meta
new file mode 100644
index 00000000..c968cad9
--- /dev/null
+++ b/Assets/Resources/SHI/Modal/PlayBar.uxml.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 92fab9ded97bb07428895b8a3bd766dc
+ScriptedImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 2
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+ script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
diff --git a/Assets/Resources/SHI/Modal/TreeList.uss b/Assets/Resources/SHI/Modal/TreeList.uss
index 4e0018c7..88715206 100644
--- a/Assets/Resources/SHI/Modal/TreeList.uss
+++ b/Assets/Resources/SHI/Modal/TreeList.uss
@@ -150,3 +150,105 @@
.visibility-off {
background-image: resource('SHI/Images/icon_visibility_off_64');
}
+
+/* ===================================
+ 세로 스크롤바 스타일
+ 슬림한 6px 너비의 커스텀 스크롤바
+ =================================== */
+.unity-scroller--vertical {
+ width: 6px; /* 슬림한 너비 */
+ margin-right: 4px;
+ margin-bottom: 0px;
+}
+
+/* 세로 스크롤바 트랙 (배경) */
+.unity-scroller--vertical .unity-base-slider__tracker {
+ background-color: rgba(255, 255, 255, 0); /* 흰색 배경 */
+ border-width: 0;
+}
+
+/* 세로 스크롤바 드래거 컨테이너 위치 조정 */
+.unity-scroller--vertical .unity-base-slider__drag-container {
+ left: 0;
+ right: 0;
+}
+
+/* 세로 스크롤바 드래거 (핸들) */
+.unity-scroller--vertical .unity-base-slider__dragger {
+ background-color: rgb(216, 216, 216); /* 밝은 회색 */
+ border-width: 0;
+ border-radius: 3px; /* 둥근 모서리 */
+ width: 6px;
+ left: 0;
+}
+
+/* 세로 스크롤바 화살표 버튼 숨김 */
+.unity-scroller--vertical .unity-repeat-button {
+ display: none;
+ width: 0;
+ height: 0;
+ min-width: 0;
+ min-height: 0;
+}
+
+/* 세로 스크롤바 슬라이더 마진 제거 */
+.unity-scroller--vertical .unity-slider {
+ margin: 0;
+}
+
+/* 세로 스크롤바 입력 필드 크기 조정 */
+.unity-scroller--vertical .unity-base-field__input {
+ width: 6px;
+ min-width: 6px;
+}
+
+/* ===================================
+ 가로 스크롤바 스타일
+ 슬림한 6px 높이의 커스텀 스크롤바
+ =================================== */
+.unity-scroller--horizontal {
+ height: 6px; /* 슬림한 높이 */
+ margin-bottom: 4px;
+ margin-right: 0px;
+}
+
+/* 가로 스크롤바 트랙 (배경) */
+.unity-scroller--horizontal .unity-base-slider__tracker {
+ background-color: rgba(255, 255, 255, 0); /* 흰색 배경 */
+ border-width: 0;
+}
+
+/* 가로 스크롤바 드래거 컨테이너 위치 조정 */
+.unity-scroller--horizontal .unity-base-slider__drag-container {
+ top: 0;
+ bottom: 0;
+}
+
+/* 가로 스크롤바 드래거 (핸들) */
+.unity-scroller--horizontal .unity-base-slider__dragger {
+ background-color: rgb(216, 216, 216); /* 밝은 회색 */
+ border-width: 0;
+ border-radius: 3px; /* 둥근 모서리 */
+ height: 6px;
+ top: 0;
+}
+
+/* 가로 스크롤바 화살표 버튼 숨김 */
+.unity-scroller--horizontal .unity-repeat-button {
+ display: none;
+ width: 0;
+ height: 0;
+ min-width: 0;
+ min-height: 0;
+}
+
+/* 가로 스크롤바 슬라이더 마진 제거 */
+.unity-scroller--horizontal .unity-slider {
+ margin: 0;
+}
+
+/* 가로 스크롤바 입력 필드 크기 조정 */
+.unity-scroller--horizontal .unity-base-field__input {
+ height: 6px;
+ min-height: 6px;
+}
diff --git a/Assets/Resources/SHI/Modal/TreeList.uxml b/Assets/Resources/SHI/Modal/TreeList.uxml
index 99693009..e48e9db0 100644
--- a/Assets/Resources/SHI/Modal/TreeList.uxml
+++ b/Assets/Resources/SHI/Modal/TreeList.uxml
@@ -1,57 +1,14 @@
-
-
-
diff --git a/Assets/Scenes/Sample/ShiPopupSample.cs b/Assets/Scenes/Sample/ShiPopupSample.cs
index 9eb7d1d5..26b9cae1 100644
--- a/Assets/Scenes/Sample/ShiPopupSample.cs
+++ b/Assets/Scenes/Sample/ShiPopupSample.cs
@@ -2,6 +2,7 @@
using Cysharp.Threading.Tasks;
using SHI.Modal.ISOP;
using SHI.Modal.NW;
+using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
@@ -80,12 +81,18 @@ public class ShiPopupSample : MonoBehaviour
}
string sa = Application.streamingAssetsPath;
- string glbPath = Path.Combine(sa, "block.glb");
+ string glbPath = Path.Combine(sa, "B16VC.glb");
string jsonPath = Path.Combine(sa, "isop_chart_short.json");
Debug.Log($"Loaded blockDetailModal:{isopModal}");
- await isopModal.LoadData(glbPath, jsonPath);
+ await isopModal.LoadData(new List {
+ Path.Combine(sa, "B11TC.glb"),
+ Path.Combine(sa, "B16VC.glb"),
+ Path.Combine(sa, "E11VC.glb"),
+ Path.Combine(sa, "E41UC.glb"),
+ Path.Combine(sa, "M11UC.glb"),
+ }, jsonPath);
}
private async UniTaskVoid SetupDataNW()
@@ -96,12 +103,17 @@ public class ShiPopupSample : MonoBehaviour
return;
}
- string sa = Application.streamingAssetsPath;
- string glbPath = Path.Combine(sa, "block.glb");
+ string sa = Application.streamingAssetsPath;
string jsonPath = Path.Combine(sa, "nw_chart.json");
Debug.Log($"Loaded blockDetailModal:{nwModal}");
- await nwModal.LoadData(glbPath, jsonPath);
+ await nwModal.LoadData(new List {
+ Path.Combine(sa, "B11TC.glb"),
+ Path.Combine(sa, "B16VC.glb"),
+ Path.Combine(sa, "E11VC.glb"),
+ Path.Combine(sa, "E41UC.glb"),
+ Path.Combine(sa, "M11UC.glb"),
+ }, jsonPath);
}
}
diff --git a/Assets/Scripts/SHI/modal/ISOP/ISOPChart.cs b/Assets/Scripts/SHI/modal/ISOP/ISOPChart.cs
index c3a72f99..f47781f7 100644
--- a/Assets/Scripts/SHI/modal/ISOP/ISOPChart.cs
+++ b/Assets/Scripts/SHI/modal/ISOP/ISOPChart.cs
@@ -42,8 +42,12 @@ namespace SHI.Modal.ISOP
///
///
[UxmlElement]
- public partial class ISOPChart : VisualElement
+ public partial class ISOPChart : VisualElement, IDisposable
{
+ #region IDisposable
+ private bool _disposed = false;
+ #endregion
+
#region 상수 (Constants)
/// 메인 UXML 파일 경로 (Resources 폴더 기준)
private const string UXML_PATH = "SHI/Modal/ISOP/ISOPChart";
@@ -525,5 +529,47 @@ namespace SHI.Modal.ISOP
return 0;
}
#endregion
+
+ #region IDisposable
+ ///
+ /// 리소스를 해제하고 이벤트 핸들러를 정리합니다.
+ ///
+ public void Dispose()
+ {
+ if (_disposed) return;
+ _disposed = true;
+
+ // 스크롤 이벤트 해제
+ if (contentScroll != null)
+ {
+ contentScroll.horizontalScroller.valueChanged -= OnHorizontalScroll;
+ }
+
+ // 외부 이벤트 정리
+ OnExpand = null;
+
+ // 동적으로 생성된 UI 요소 정리
+ monthsLayer?.Clear();
+ weeksLayer?.Clear();
+ daysLayer?.Clear();
+ timelineContent?.Clear();
+
+ // 데이터 정리
+ tasks?.Clear();
+ tasks = null;
+
+ // UI 참조 정리
+ _expandBtn = null;
+ taskRowTemplate = null;
+ root = null;
+ headerTitle = null;
+ headerTimeline = null;
+ monthsLayer = null;
+ weeksLayer = null;
+ daysLayer = null;
+ timelineContent = null;
+ contentScroll = null;
+ }
+ #endregion
}
}
diff --git a/Assets/Scripts/SHI/modal/ISOP/ISOPModal.cs b/Assets/Scripts/SHI/modal/ISOP/ISOPModal.cs
index 57f7d091..4d17acf6 100644
--- a/Assets/Scripts/SHI/modal/ISOP/ISOPModal.cs
+++ b/Assets/Scripts/SHI/modal/ISOP/ISOPModal.cs
@@ -48,15 +48,15 @@ namespace SHI.Modal.ISOP
[SerializeField]
public UIDocument uiDocument;
- private VisualElement content;
+ private VisualElement? content;
- private TreeList listView;
- private ISOPModelView modelView;
- private ISOPChart chartView;
+ private TreeList? listView;
+ private ISOPModelView? modelView;
+ private ISOPChart? chartView;
- private Button closeBtn;
- private Button showTreeBtn;
- private Button dragBtn;
+ private Button? closeBtn;
+ private Button? showTreeBtn;
+ private Button? dragBtn;
private CancellationTokenSource? _cts;
private bool _suppressSelection = false;
@@ -79,6 +79,14 @@ namespace SHI.Modal.ISOP
private float _lastModelFlexGrow = 1f;
private float _lastChartFlexGrow = 1f;
+ // GeometryChangedEvent 콜백 (해제용)
+ private EventCallback? _contentGeometryChangedCallback;
+
+ // 로딩 UI
+ private VisualElement? _loadingOverlay;
+ private VisualElement? _loadingSpinner;
+ private IVisualElementScheduledItem? _spinnerAnimation;
+
private void OnEnable()
{
@@ -140,8 +148,12 @@ namespace SHI.Modal.ISOP
}
initDrag(root);
-
+
_expanded = ExpandedSide.None;
+
+ // 로딩 UI 참조
+ _loadingOverlay = root.Q("loading-overlay");
+ _loadingSpinner = root.Q("loading-spinner");
}
private void initDrag(VisualElement root)
@@ -158,103 +170,107 @@ namespace SHI.Modal.ISOP
//dragBtn.AddManipulator(new HorizontalDragManipulator());
- // 드래그 시작
- dragBtn.RegisterCallback((evt) =>
- {
- // 좌클릭만 처리 (0)
- if (evt.button != 0) return;
-
- Debug.Log("Drag Started (PointerDown) - captured");
-
- // 포인터 캡처
- _isDragging = true;
- _activePointerId = evt.pointerId;
- dragBtn.CapturePointer(_activePointerId);
-
- // 포인터가 drag 버튼의 어느 위치를 눌렀는지 계산 (center 기준)
- // evt.position은 content 기준 좌표
- var dragCenterX = dragBtn.layout.x + dragBtn.layout.width * 0.5f;
- _dragOffset = evt.position.x - dragCenterX;
-
- evt.StopImmediatePropagation();
- }, TrickleDown.TrickleDown);
-
- // 전역 포인터 무브로 위치 추적 (root에 등록)
- dragBtn.RegisterCallback((evt) =>
- {
- if (!_isDragging) return;
- if (evt.pointerId != _activePointerId) return;
- //Debug.Log($"Dragging... evt.pointerId:{evt.pointerId}, _activePointerId:{_activePointerId}");
-
-
- // evt.position은 content 기준 좌표
- float pointerX = evt.position.x;
- float centerX = pointerX - _dragOffset;
- ApplyDragPosition(content, dragBtn, centerX);
- evt.StopImmediatePropagation();
- }, TrickleDown.TrickleDown);
-
- // 드래그 종료 (마우스 업 또는 포인터 캔슬)
- dragBtn.RegisterCallback((evt) =>
- {
- if (!_isDragging) return;
- if (evt.pointerId != _activePointerId) return;
-
- Debug.Log("Drag Ended");
-
- _isDragging = false;
- if (_activePointerId != -1)
- {
- try { dragBtn.ReleasePointer(_activePointerId); } catch { }
- }
- _activePointerId = -1;
- evt.StopImmediatePropagation();
- }, TrickleDown.TrickleDown);
-
- dragBtn.RegisterCallback((evt) =>
- {
- if (!_isDragging) return;
- if (evt.pointerId != _activePointerId) return;
-
- _isDragging = false;
- if (_activePointerId != -1)
- {
- try { dragBtn.ReleasePointer(_activePointerId); } catch { }
- }
- _activePointerId = -1;
- evt.StopImmediatePropagation();
- }, TrickleDown.TrickleDown);
+ // 드래그 이벤트 등록
+ dragBtn.RegisterCallback(OnDragPointerDown, TrickleDown.TrickleDown);
+ dragBtn.RegisterCallback(OnDragPointerMove, TrickleDown.TrickleDown);
+ dragBtn.RegisterCallback(OnDragPointerUp, TrickleDown.TrickleDown);
+ dragBtn.RegisterCallback(OnDragPointerCancel, TrickleDown.TrickleDown);
// 초기화 및 레이아웃 변경 시 재계산
- bool initialized = false;
- content.RegisterCallback((evt) =>
- {
- // 드래그 중에는 GeometryChanged 이벤트 무시
- if (_isDragging) return;
-
- // 초기화: treeList의 레이아웃이 계산될 때까지 대기
- if (!initialized)
- {
- if (listView == null || listView.layout.width <= 0)
- {
- return; // 아직 레이아웃이 계산되지 않음
- }
- initialized = true;
- }
- UpdateDragAndPanels(content, dragBtn);
- });
+ _contentGeometryChangedCallback = OnContentGeometryChanged;
+ content.RegisterCallback(_contentGeometryChangedCallback);
}
}
}
+ private void OnDragPointerDown(PointerDownEvent evt)
+ {
+ // 좌클릭만 처리 (0)
+ if (evt.button != 0) return;
+
+ Debug.Log("Drag Started (PointerDown) - captured");
+
+ // 포인터 캡처
+ _isDragging = true;
+ _activePointerId = evt.pointerId;
+ dragBtn.CapturePointer(_activePointerId);
+
+ // 포인터가 drag 버튼의 어느 위치를 눌렀는지 계산 (center 기준)
+ // evt.position은 content 기준 좌표
+ var dragCenterX = dragBtn.layout.x + dragBtn.layout.width * 0.5f;
+ _dragOffset = evt.position.x - dragCenterX;
+
+ evt.StopImmediatePropagation();
+ }
+
+ private void OnDragPointerMove(PointerMoveEvent evt)
+ {
+ if (!_isDragging) return;
+ if (evt.pointerId != _activePointerId) return;
+
+ // evt.position은 content 기준 좌표
+ float pointerX = evt.position.x;
+ float centerX = pointerX - _dragOffset;
+ ApplyDragPosition(content, dragBtn, centerX);
+ evt.StopImmediatePropagation();
+ }
+
+ private void OnDragPointerUp(PointerUpEvent evt)
+ {
+ if (!_isDragging) return;
+ if (evt.pointerId != _activePointerId) return;
+
+ Debug.Log("Drag Ended");
+
+ _isDragging = false;
+ if (_activePointerId != -1)
+ {
+ try { dragBtn.ReleasePointer(_activePointerId); } catch { }
+ }
+ _activePointerId = -1;
+ evt.StopImmediatePropagation();
+ }
+
+ private void OnDragPointerCancel(PointerCancelEvent evt)
+ {
+ if (!_isDragging) return;
+ if (evt.pointerId != _activePointerId) return;
+
+ _isDragging = false;
+ if (_activePointerId != -1)
+ {
+ try { dragBtn.ReleasePointer(_activePointerId); } catch { }
+ }
+ _activePointerId = -1;
+ evt.StopImmediatePropagation();
+ }
+
+ private bool _geometryInitialized = false;
+ private void OnContentGeometryChanged(GeometryChangedEvent evt)
+ {
+ // 드래그 중에는 GeometryChanged 이벤트 무시
+ if (_isDragging) return;
+
+ // 초기화: treeList의 레이아웃이 계산될 때까지 대기
+ if (!_geometryInitialized)
+ {
+ if (listView == null || listView.layout.width <= 0)
+ {
+ return; // 아직 레이아웃이 계산되지 않음
+ }
+ _geometryInitialized = true;
+ }
+ UpdateDragAndPanels(content, dragBtn);
+ }
+
///
/// glTF 모델과 간트 데이터를 하위 뷰에 로드하고, 선택 동기화를 위한 매핑을 구축합니다.
///
/// glTF/glb 파일 경로.
/// 간트 데이터셋 경로.
/// 외부 취소 토큰.
- public async UniTask LoadData(string gltfPath, string ganttPath, CancellationToken externalCt = default)
+ public async UniTask LoadData(List gltfPaths, string ganttPath, CancellationToken externalCt = default)
{
if(modelView == null)
{
@@ -263,38 +279,103 @@ namespace SHI.Modal.ISOP
{
await UniTask.Yield();
}
-
+
}
- Debug.Log($"ISOPModal: LoadData {gltfPath}");
-
+ Debug.Log($"ISOPModal: LoadData {string.Join(", ", gltfPaths)}, {ganttPath}");
- // 이전 작업 취소
- if (_cts != null)
- {
- try { _cts.Cancel(); } catch { }
- _cts.Dispose();
- }
- _cts = CancellationTokenSource.CreateLinkedTokenSource(externalCt);
- var ct = _cts.Token;
+ // 로딩 표시
+ ShowLoading(true);
- // 모델/리스트 로드
- IEnumerable items = Array.Empty();
- if (modelView != null)
+ try
{
- try
+ // 이전 작업 취소
+ if (_cts != null)
{
- items = await modelView.LoadModelAsync(gltfPath, ct);
+ try { _cts.Cancel(); } catch { }
+ _cts.Dispose();
}
- catch (OperationCanceledException) { }
+ _cts = CancellationTokenSource.CreateLinkedTokenSource(externalCt);
+ var ct = _cts.Token;
+
+ // 모델/리스트 로드
+ IEnumerable items = Array.Empty();
+ if (modelView != null)
+ {
+ try
+ {
+ items = await modelView.LoadModelAsync(gltfPaths, ct);
+ }
+ catch (OperationCanceledException) { }
+ }
+
+ BuildKeyMaps(items);
+
+ if (listView != null) listView.SetData(items.ToList());
+ if (chartView != null) chartView.Load(ganttPath);
+ }
+ finally
+ {
+ // 로딩 숨기기
+ ShowLoading(false);
+ }
+ }
+
+ #region 로딩 UI (Loading UI)
+ ///
+ /// 로딩 오버레이와 스피너 애니메이션을 표시하거나 숨깁니다.
+ ///
+ private void ShowLoading(bool show)
+ {
+ if (_loadingOverlay == null) return;
+
+ if (show)
+ {
+ _loadingOverlay.AddToClassList("visible");
+ StartSpinnerAnimation();
+ }
+ else
+ {
+ _loadingOverlay.RemoveFromClassList("visible");
+ StopSpinnerAnimation();
+ }
+ }
+
+ ///
+ /// 스피너 회전 애니메이션 시작
+ ///
+ private void StartSpinnerAnimation()
+ {
+ if (_loadingSpinner == null) return;
+
+ StopSpinnerAnimation();
+
+ float angle = 0f;
+ _spinnerAnimation = _loadingSpinner.schedule.Execute(() =>
+ {
+ angle = (angle + 10f) % 360f;
+ _loadingSpinner.style.rotate = new Rotate(Angle.Degrees(angle));
+ }).Every(16); // ~60fps
+ }
+
+ ///
+ /// 스피너 회전 애니메이션 중지
+ ///
+ private void StopSpinnerAnimation()
+ {
+ if (_spinnerAnimation != null)
+ {
+ _spinnerAnimation.Pause();
+ _spinnerAnimation = null;
}
- BuildKeyMaps(items);
-
- if (listView != null) listView.SetData(items.ToList());
- if (chartView != null) chartView.Load(ganttPath);
+ if (_loadingSpinner != null)
+ {
+ _loadingSpinner.style.rotate = new Rotate(Angle.Degrees(0));
+ }
}
+ #endregion
private void BuildKeyMaps(IEnumerable items)
{
@@ -595,13 +676,24 @@ namespace SHI.Modal.ISOP
private void OnDestroy()
{
+ // CancellationTokenSource 정리
+ if (_cts != null)
+ {
+ try { _cts.Cancel(); } catch { }
+ _cts.Dispose();
+ _cts = null;
+ }
+
+ // listView 이벤트 해제 및 Dispose
if (listView != null)
{
listView.OnSelectionChanged -= OnListItemSelectionChanged;
listView.OnClosed -= OnListClosed;
listView.OnVisibilityChanged -= OnListVisibilityChanged;
+ listView.Dispose();
}
+ // modelView 이벤트 해제 및 Dispose
if (modelView != null)
{
modelView.OnItemSelected -= OnModelItemSelected;
@@ -609,13 +701,56 @@ namespace SHI.Modal.ISOP
modelView.Dispose();
}
+ // chartView 이벤트 해제 및 Dispose
if (chartView != null)
{
chartView.OnExpand -= ToggleExpandChart;
+ chartView.Dispose();
}
+ // 버튼 이벤트 해제
if (showTreeBtn != null) showTreeBtn.clicked -= OnClickShowTree;
if (closeBtn != null) closeBtn.clicked -= OnClickClose;
+
+ // dragBtn 포인터 이벤트 해제
+ if (dragBtn != null)
+ {
+ dragBtn.UnregisterCallback(OnDragPointerDown, TrickleDown.TrickleDown);
+ dragBtn.UnregisterCallback(OnDragPointerMove, TrickleDown.TrickleDown);
+ dragBtn.UnregisterCallback(OnDragPointerUp, TrickleDown.TrickleDown);
+ dragBtn.UnregisterCallback(OnDragPointerCancel, TrickleDown.TrickleDown);
+ }
+
+ // content GeometryChangedEvent 해제
+ if (content != null && _contentGeometryChangedCallback != null)
+ {
+ content.UnregisterCallback(_contentGeometryChangedCallback);
+ _contentGeometryChangedCallback = null;
+ }
+
+ // 딕셔너리 정리
+ _keyToId.Clear();
+ _idToKey.Clear();
+
+ // 드래그 상태 초기화
+ _isDragging = false;
+ _activePointerId = -1;
+ _dragOffset = 0f;
+ _geometryInitialized = false;
+
+ // 로딩 UI 정리
+ StopSpinnerAnimation();
+ _loadingOverlay = null;
+ _loadingSpinner = null;
+
+ // UI 참조 정리
+ content = null;
+ listView = null;
+ modelView = null;
+ chartView = null;
+ closeBtn = null;
+ showTreeBtn = null;
+ dragBtn = null;
}
}
diff --git a/Assets/Scripts/SHI/modal/ISOP/ISOPModelView.cs b/Assets/Scripts/SHI/modal/ISOP/ISOPModelView.cs
index 7bc19612..c0808ee6 100644
--- a/Assets/Scripts/SHI/modal/ISOP/ISOPModelView.cs
+++ b/Assets/Scripts/SHI/modal/ISOP/ISOPModelView.cs
@@ -46,8 +46,12 @@ namespace SHI.Modal.ISOP
///
///
[UxmlElement]
- public partial class ISOPModelView : VisualElement
+ public partial class ISOPModelView : VisualElement, IDisposable
{
+ #region IDisposable
+ private bool _disposed = false;
+ #endregion
+
#region 외부 이벤트 (Public Events)
///
/// 뷰 내부에서 항목이 선택될 때 발생합니다.
@@ -156,35 +160,55 @@ namespace SHI.Modal.ISOP
}
///
- /// 주어진 경로의 glTF 모델을 비동기로 로드하고, UI 트리에 사용할 계층 항목을 생성합니다.
+ /// 주어진 경로들의 glTF 모델을 비동기로 로드하고, UI 트리에 사용할 계층 항목을 생성합니다.
///
- public async UniTask> LoadModelAsync(string path, CancellationToken ct)
+ /// 로드할 glTF/glb 파일 경로 목록
+ /// 취소 토큰
+ /// 로드된 모델들의 계층 항목 목록
+ public async UniTask> LoadModelAsync(List paths, CancellationToken ct)
{
- Debug.Log($"ISOPModelView.LoadModelAsync: {path}");
- Dispose();
+ Debug.Log($"ISOPModelView.LoadModelAsync: {paths?.Count ?? 0} files");
+
+ CleanupForReload();
await UniTask.DelayFrame(1);
EnsureCameraAndTargetTexture();
var items = new List();
- var gltf = new GltfImport();
- var success = await gltf.Load(path, new ImportSettings(), ct);
- if (!success)
+
+ if (paths == null || paths.Count == 0)
{
- Debug.LogError($"glTFast Load failed: {path}");
+ Debug.LogWarning("ISOPModelView.LoadModelAsync: No paths provided");
return items;
}
- if(_root == null) _root = new GameObject("ISOPModelViewRoot");
+ if (_root == null) _root = new GameObject("ISOPModelViewRoot");
_root.layer = modelLayer;
- var sceneOk = await gltf.InstantiateMainSceneAsync(_root.transform);
- if (!sceneOk)
+
+ // 각 파일을 순차적으로 로드
+ foreach (var path in paths)
{
- Debug.LogError("InstantiateMainSceneAsync failed");
- return items;
+ if (string.IsNullOrEmpty(path)) continue;
+
+ Debug.Log($"ISOPModelView.LoadModelAsync: Loading {path}");
+ var gltf = new GltfImport();
+ var success = await gltf.Load(path, new ImportSettings(), ct);
+ if (!success)
+ {
+ Debug.LogError($"glTFast Load failed: {path}");
+ continue;
+ }
+
+ var sceneOk = await gltf.InstantiateMainSceneAsync(_root.transform);
+ if (!sceneOk)
+ {
+ Debug.LogError($"InstantiateMainSceneAsync failed: {path}");
+ continue;
+ }
}
SetLayerRecursive(_root, modelLayer);
+ // 로드된 모든 자식에서 TreeListItemData 생성
if (_root != null)
{
for (int i = 0; i < _root.transform.childCount; i++)
@@ -600,8 +624,13 @@ namespace SHI.Modal.ISOP
}
}
- public void Dispose()
+ ///
+ /// 모델 재로드를 위한 리소스 정리.
+ /// Dispose()와 달리 UI 참조(_renderContainer, _expandBtn)는 유지합니다.
+ ///
+ private void CleanupForReload()
{
+ // 머티리얼 정리 (인스턴스 머티리얼 삭제)
foreach (var kv in _originalSharedByRenderer)
{
var r = kv.Key;
@@ -616,11 +645,14 @@ namespace SHI.Modal.ISOP
}
_originalSharedByRenderer.Clear();
_idToObject.Clear();
+
+ // 모델 루트 삭제
if (_root != null) UnityEngine.Object.Destroy(_root);
_root = null;
_focusedId = null;
_wireframeApplied = false;
+ // 카메라 및 렌더텍스처 정리
if (_viewCamera != null)
{
if (_rt != null && _viewCamera.targetTexture == _rt)
@@ -629,6 +661,7 @@ namespace SHI.Modal.ISOP
}
_viewCamera.enabled = false;
}
+
if (_rt != null)
{
_rt.Release();
@@ -636,13 +669,95 @@ namespace SHI.Modal.ISOP
_rt = null;
}
- //ISOPModelViewRig란 이름의 게임오브젝트도 같이 삭제
+ // 드래그 상태 초기화
+ _mmbDragging = false;
+ _rmbDragging = false;
+
+ // ID 시드 초기화
+ itemIdSeed = 1;
+
+ // 게임오브젝트 정리
+ var rigGo = GameObject.Find("ISOPModelViewRig");
+ if (rigGo != null) UnityEngine.Object.Destroy(rigGo);
+ _viewCamera = null;
+
+ var rootGo = GameObject.Find("ISOPModelViewRoot");
+ if (rootGo != null) UnityEngine.Object.Destroy(rootGo);
+ }
+
+ public void Dispose()
+ {
+ if (_disposed) return;
+ _disposed = true;
+
+ // 마우스 이벤트 해제
+ UnregisterCallback(OnMouseDown);
+ UnregisterCallback(OnMouseUp);
+ UnregisterCallback(OnMouseMove);
+ UnregisterCallback(OnWheel);
+ UnregisterCallback(OnGeometryChanged);
+
+ // 외부 이벤트 정리
+ OnItemSelected = null;
+ OnExpand = null;
+
+ // 머티리얼 정리 (인스턴스 머티리얼 삭제)
+ foreach (var kv in _originalSharedByRenderer)
+ {
+ var r = kv.Key;
+ if (r == null) continue;
+ var originals = kv.Value;
+ var mats = r.materials;
+ for (int m = 0; m < mats.Length; m++)
+ {
+ if (mats[m] != null) UnityEngine.Object.Destroy(mats[m]);
+ }
+ r.materials = originals;
+ }
+ _originalSharedByRenderer.Clear();
+ _idToObject.Clear();
+
+ // 모델 루트 삭제
+ if (_root != null) UnityEngine.Object.Destroy(_root);
+ _root = null;
+ _focusedId = null;
+ _wireframeApplied = false;
+ _wireframeMat = null; // Resources에서 로드한 것은 Destroy하지 않음
+
+ // 카메라 및 렌더텍스처 정리
+ if (_viewCamera != null)
+ {
+ if (_rt != null && _viewCamera.targetTexture == _rt)
+ {
+ _viewCamera.targetTexture = null;
+ }
+ _viewCamera.enabled = false;
+ }
+ _viewCamera = null;
+
+ if (_rt != null)
+ {
+ _rt.Release();
+ UnityEngine.Object.Destroy(_rt);
+ _rt = null;
+ }
+
+ // 드래그 상태 초기화
+ _mmbDragging = false;
+ _rmbDragging = false;
+
+ // ID 시드 초기화
+ itemIdSeed = 1;
+
+ // UI 참조 정리
+ _renderContainer = null;
+ _expandBtn = null;
+
+ // 게임오브젝트 정리
var rigGo = GameObject.Find("ISOPModelViewRig");
if (rigGo != null) UnityEngine.Object.Destroy(rigGo);
var rootGo = GameObject.Find("ISOPModelViewRoot");
if (rootGo != null) UnityEngine.Object.Destroy(rootGo);
-
-
}
// 유틸리티 메서드
diff --git a/Assets/Scripts/SHI/modal/NW/NWChart.cs b/Assets/Scripts/SHI/modal/NW/NWChart.cs
index 74a77094..8627cff4 100644
--- a/Assets/Scripts/SHI/modal/NW/NWChart.cs
+++ b/Assets/Scripts/SHI/modal/NW/NWChart.cs
@@ -64,8 +64,12 @@ namespace SHI.Modal.NW
///
///
[UxmlElement]
- public partial class NWChart : VisualElement
+ public partial class NWChart : VisualElement, IDisposable
{
+ #region IDisposable
+ private bool _disposed = false;
+ #endregion
+
private const string UXML_PATH = "SHI/Modal/NW/NWChart";
public Action? OnExpand;
@@ -123,7 +127,9 @@ namespace SHI.Modal.NW
private bool isDragging;
private DateTime projectStartDate;
+ public DateTime ProjectStartDate => projectStartDate;
private DateTime projectEndDate;
+ public DateTime ProjectEndDate => projectEndDate;
private int totalDays;
private float canvasHeight;
@@ -142,10 +148,15 @@ namespace SHI.Modal.NW
_expandBtn = this.Q