55using System . IO ;
66using System . Linq ;
77using System . Runtime ;
8+ using System . Runtime . InteropServices ;
89using System . Text ;
10+ using System . Threading ;
911using System . Threading . Tasks ;
1012using System . Windows . Forms ;
13+ using Windows . Win32 ;
14+ using Windows . Win32 . Foundation ;
1115
1216namespace ObsidianShell
1317{
@@ -20,15 +24,15 @@ public Obsidian(Settings settings)
2024 _settings = settings ;
2125 }
2226
23- public void OpenFile ( string path )
27+ public async Task OpenFile ( string path )
2428 {
2529 switch ( _settings . OpenMode )
2630 {
2731 case OpenMode . VaultFallback :
2832 {
2933 if ( IsFileInVault ( path ) )
3034 {
31- OpenFileInVault ( path ) ;
35+ await OpenFileInVault ( path ) ;
3236 }
3337 else
3438 {
@@ -40,17 +44,17 @@ public void OpenFile(string path)
4044 {
4145 if ( IsFileInVault ( path ) )
4246 {
43- OpenFileInVault ( path ) ;
47+ await OpenFileInVault ( path ) ;
4448 }
4549 else
4650 {
47- OpenFileInRecent ( path ) ;
51+ await OpenFileInRecent ( path ) ;
4852 }
4953 break ;
5054 }
5155 case OpenMode . Recent :
5256 {
53- OpenFileInRecent ( path ) ;
57+ await OpenFileInRecent ( path ) ;
5458 break ;
5559 }
5660 }
@@ -63,7 +67,7 @@ private static string PercentEncode(string text)
6367 return Uri . EscapeDataString ( text ) ;
6468 }
6569
66- private void OpenFileInVault ( string path , string vaultPath = null )
70+ private async Task OpenFileInVault ( string path , string vaultPath = null )
6771 {
6872 if ( _settings . EnableAdvancedURI is false )
6973 {
@@ -73,7 +77,7 @@ private void OpenFileInVault(string path, string vaultPath = null)
7377 {
7478 vaultPath = vaultPath ?? GetFileVaultPath ( path ) ;
7579 string vault = GetVaultName ( vaultPath ) ;
76- string filename = Utils . GetRelativePath ( vaultPath , path ) ;
80+ string filepath = Utils . GetRelativePath ( vaultPath , path ) ;
7781
7882 ObsidianOpenMode obsidianOpenMode = _settings . ObsidianDefaultOpenMode ;
7983 if ( Utils . IsKeyPressed ( Keys . ControlKey ) )
@@ -85,20 +89,60 @@ private void OpenFileInVault(string path, string vaultPath = null)
8589 if ( Utils . IsKeyPressed ( Keys . LWin ) || Utils . IsKeyPressed ( Keys . RWin ) )
8690 obsidianOpenMode = _settings . ObsidianWinOpenMode ;
8791
88- string openmode = obsidianOpenMode switch
92+ if ( obsidianOpenMode is ObsidianOpenMode . NewWindow )
8993 {
90- ObsidianOpenMode . CurrentTab => "false" ,
91- ObsidianOpenMode . NewTab => "tab" ,
92- ObsidianOpenMode . NewWindow => "window" ,
93- ObsidianOpenMode . NewPane => "split" ,
94- ObsidianOpenMode . HoverPopover => "popover" ,
95- _ => throw new ArgumentException ( )
96- } ;
97-
98- Process . Start ( $ "obsidian://advanced-uri?vault={ PercentEncode ( vault ) } &filepath={ PercentEncode ( filename ) } &openmode={ openmode } ") ;
94+ await OpenFileInNewWindow ( vault , filepath ) ;
95+ }
96+ else
97+ {
98+ string openmode = obsidianOpenMode switch
99+ {
100+ ObsidianOpenMode . CurrentTab => "false" ,
101+ ObsidianOpenMode . NewTab => "tab" ,
102+ //ObsidianOpenMode.NewWindow => "window",
103+ ObsidianOpenMode . NewPane => "split" ,
104+ ObsidianOpenMode . HoverPopover => "popover" ,
105+ ObsidianOpenMode . VaultAndNewWindow => "window" ,
106+ _ => throw new ArgumentException ( )
107+ } ;
108+ Process . Start ( $ "obsidian://advanced-uri?vault={ PercentEncode ( vault ) } &filepath={ PercentEncode ( filepath ) } &openmode={ openmode } ") ;
109+ }
99110 }
100111 }
101112
113+ private async Task OpenFileInNewWindow ( string vault , string filepath )
114+ {
115+ List < WindowVisualState > states = Utils . EnumerateProcessWindowHandles ( "Obsidian" , "Chrome_WidgetWin_1" ) . Select ( w => new WindowVisualState ( w ) ) . ToList ( ) ;
116+ Process . Start ( $ "obsidian://advanced-uri?vault={ PercentEncode ( vault ) } &filepath={ PercentEncode ( filepath ) } &openmode=window") ;
117+
118+ Stopwatch stopwach = Stopwatch . StartNew ( ) ;
119+ do
120+ {
121+ await Task . Delay ( 50 ) ;
122+ // This is not precise enough. If the vault hasn't been opend before, Obsidian will create two or more windows.
123+ // But that case is hard to detect.
124+ if ( Utils . EnumerateProcessWindowHandles ( "Obsidian" , "Chrome_WidgetWin_1" ) . Count ( ) > states . Count )
125+ {
126+ // 250~477ms
127+ Debug . WriteLine ( $ "Found new window after { stopwach . ElapsedMilliseconds } ms") ;
128+ break ;
129+ }
130+ } while ( stopwach . ElapsedMilliseconds < 10000 ) ;
131+
132+ foreach ( WindowVisualState state in states )
133+ {
134+ state . Restore ( ) ;
135+ }
136+
137+ /*
138+ // If not specify Chrome_WidgetWin_1, we will get Chrome_WidgetWin_0.
139+ HWND newWindow = Utils.EnumerateProcessWindowHandles("Obsidian", "Chrome_WidgetWin_1").Where(x => !states.Select(x => x.Handle).Contains(x)).First();
140+ Debug.WriteLine($"New window: {new WindowVisualState(newWindow)}");
141+
142+ PInvoke.BringWindowToTop(newWindow);
143+ */
144+ }
145+
102146 private static string GetVaultName ( string path )
103147 {
104148 return Path . GetFileName ( path ) ;
@@ -142,7 +186,7 @@ private void OpenFileByFallback(string path)
142186 Process . Start ( editor , String . Format ( _settings . FallbackMarkdownEditorArguments , $@ """{ path } """));
143187 }
144188
145- private void OpenFileInRecent(string path)
189+ private async Task OpenFileInRecent(string path)
146190 {
147191 // hard link stays valid when the source file is deleted;
148192 // symbolic link requires SeCreateSymbolicLinkPrivilege;
@@ -177,7 +221,7 @@ private void OpenFileInRecent(string path)
177221 path_in_recent = CreateLinkInRecent( directory . Parent , false ) + '\\ ' + directory . Name ;
178222 }
179223
180- OpenFileInVault ( path_in_recent , _settings . RecentVault ) ;
224+ await OpenFileInVault ( path_in_recent , _settings . RecentVault ) ;
181225 }
182226
183227 private static string FormatLinkName( string prefixed_name , bool explicitDirectory )
0 commit comments