From 9e85571ac4d4c53963fc051c035ffb9230e09f68 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Tue, 3 Sep 2019 15:57:08 -0700 Subject: [PATCH 01/25] Spec version 1 for set terminal initial position --- .../images/Image1.png | Bin 0 -> 22238 bytes .../images/ProfileSnapshot.png | Bin 0 -> 26198 bytes .../spec.md | 87 ++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 doc/specs/#1043 - Set the initial position of the Terminal/images/Image1.png create mode 100644 doc/specs/#1043 - Set the initial position of the Terminal/images/ProfileSnapshot.png create mode 100644 doc/specs/#1043 - Set the initial position of the Terminal/spec.md diff --git a/doc/specs/#1043 - Set the initial position of the Terminal/images/Image1.png b/doc/specs/#1043 - Set the initial position of the Terminal/images/Image1.png new file mode 100644 index 0000000000000000000000000000000000000000..df249ca4567e39b6c2f345356d51900eb198fbed GIT binary patch literal 22238 zcmeFY_g52L!!;ZfQS9|nK#CM8DkxP#v(k$MLN6*!KoAI_hhRmehALepln@{S0@A@k zhiFI$Jt!q40U{-YBqY4K?&n$G`u>7{bE=s^Gg zaKyy;4hR6)D+K`TaX28vKa(5wEQtRn009}@0#py5r|=I1eQsLZ1ORGM4(&YH$3GSh zGIoRj07t+6`x7{$U>XMi*g;J0+_a5wTbXvPta6T&+6qgVve_BcEYf~7QnSM3OG z$!Y@3_qK0lK5^l0(PLMr9o(LdNXBHx1|ctcyyqCV(+tDga_5ZL8|*w!ac$eFIXgYo zqTA12wJfPLQtRg#uxXeMc3;PKZk}^W>xPeh%*KeXPD!mjk>3_nwz8L7SNHDUj-640 z0n0!?97f{orW?TZq+88qt%nr>l;N7e@y9kfBqKmb)a|^+O@aac(jLh>1G{VRT@~)y z%+1(vI9(F9OeMy0#%V$i-efQy?rNsi4=uu+VLa<5vVs!1h-?pwICuCN_7 zn$h5?$Fjyvg4v)&-P+Hp9j^hhBViqDIV$elvfa6!z}4T?yp3H7u{w6c9lk&vaOW-# z=?%QEJoW_Tr3c!IZ=A2SpNVhWY>p+W!dV_Ft*kd8t6sz5PNq5TcMjU|;_0mYl4rCf z9ZEN5nBk--#(c&C0cSQ%Zw;qq(!O2DM{>8NMsLyxF50OSfj3SO2IPmq^@|6wJkS$qz{8u&2Fr3w!r5X$03(%mI;hGM?cpNvz)S1h3r$Pa8~5uKc}w7%zNxE z0wa3o^ltRN@5*yyZZo*rAgfcx8TenV4H78_m9{dwl8|o+(FD%jl6V4_HTe0G>$8;4 z;*l}gvoik~Ho{)9TLQ*eegl5O=Z#_;-FNy`O9L|*!B34I*GpF}`JP@T-&-j7+$3r-a~I^gK6Op+)m$F9M@`{V ziaR?K>rR}*lU?V5p9(cIiB)FA3j9FpMFaVtkytBTD2yZ*WC@R0nXQI9mn|;uJsv05 z+w0|OTPWizI%leT-pX>G`}KD|KHbK>xHpw{U8%sY^-FXCo`mNYS~00T>iVK^{O#q7 zh?rIIoBe66X?}x!c_I)^j1JM;rAy)o=PLY)3+&oxBuU4zd`kb%E@It9yu)(k!sA%A+Ql%#cf5NACy58mP7 zilP@79f%@-Yeb<0*nla5ZV{9uTLSiU-t8-Yi2I%PEc3)bVeT1HZe4r3j8=#1c$Wj zHx;f`70vIVs~$XD_7Q}^U86sZL2eW_JLFj}8oOE;88CDji>c0p`ej-gIddofb2*qv zZnro&h4)Dj8?e&>rTAnuU<$ubIb=OcjRX8;Os53^8Y?Zw^%%U3$+L ze*uQ5{GG)iJqqJm|2bWr^P-5oMsy>-iPgJXeJFasCeOYe zojH7RO8!7ER@i~co3j6(2WO-}d{1J8f)pRK*KC-s!gS@AtVF-@~El9bt~~VldP8?f!lbkL0shVlA;sC zQLjMF*D)uOLM$&hD9|gOh!%115*(8acwve4H4gfzl{l%<)9fxdnF0;D>01I?*fUZn zqhIKO>qChoyG$ZPQh5={HiP@(ZyOg3UA}`kW(`#>*u-P5-K}L#gUbAD`euG%j>fv~ zT8~b{#aM-UcQ2p)Z2$Lk%!i zru@&;W_%r0yGaEelcVYKw&by!XK}@x^MoOIp?yGK{!_3`OQ@j#kMuNhNnI0?-GY|O z6P=14{uKbk zdkW2G`3cF|Y|@n3=wHKWs1EZFUIp8i31dkGX;3W6Ixp8Saeg;RO;O|uYN`vIB8dYT zS|nzjC*cfx`#0GwFJ`nuM2=t}P!upE58{iDY)yoIqI!!=#>ZBZHXTGd;IbCwS@ttq z+^t|`ui@2-!J)YP)KFbqYRO2dZfoJ~b9w}6BaIMWb6iue|Hv3&t~dbO_tKQhn?aa7 z&p2~}y0+tTsSJRY^HO3oQ7w`sVq}R8t5(yMmN$W7l;g-bL=Wz%30VrN-^~8EO0fmL{;AZb? zHkf$aQGn`%;-keK z(*Cp580U3~xhAc~jeKV8Y`PZH#g)6Gq7UItxjv6~ZI`^91DFRWVRX(In0>q}`YJII zZ@N!Agmlew^Rs7$CYwCEsrt!HNZ7Abl~HPV3%zlVNesR`PWs?^gfo+45--1EZqj1& zToP?)^&7`vf3`5lHpKYL(X)@Nn38;?Pkz5Mkt8T6@_O*HIrWj3DP98!g(qJd@;loS zW8k19&?~@rrF`VJMfO&)Auy#xv||kdsWcN1kT5+CV#w7=P_EjxQBao9O$5QC!qT*T zPnIG!PyVcud94O5vAn#_y>7)4(~s9L1bEq zm;$4TOP~F@uBEyvS!cqF4LLcDZ@OaUELU>=VNc z1SN+)Z8}s9)2B?-a0ZSp^wN4de)F1owso9AW=1HP7 zd_*CArF+{!=v34{-^b#Ypb@qtEh;RG70bDvtxCGP3(FFL#RcGIQsDB%JJYh;dUp&h zGFh<7XL5kw!&$gTD^8oe%-SfS!=McKGPUGlWj&#T>mMT$vPJ|stt1{?~Zi?WV^b*2rM)SN`XFo zQ4j{$?UO`UWB4Q+l5Y4od-gdiZB)Z;zWGXzjy!Ju1Bq!Y(TVDfyAR|!c5Uh}xWp6* z{51Ez@ujXLF?Sg>iUzrjij=MT+dd@VSI+%Rr060XQz~QAn3aUA{&Rpt8)lxjc{e@% z^mF~MPQ^l1X71p{h7pQsI~X2gKs{(zMTUq&rw-<$Cerw4R4_jE^zgXvKf z)7Nag!=Wa{zjZ_Vz4Qww(JSX^UV9Sr#q0~1>k=ZZC>}Qo(ky+a+g~ip$ z&@Z+&AOj0)RxtX;dSyi;J!XI~uvegvVDjZ8GvM5$?O#Oqr6ab<>UVrUgZfS#zr!2y zn0st0`leNr%Fv68AnFvwtnxxNPs(Qa3DVZoFMHg!UDq3Vy{H`-*p5AS{%(6TZh)#y z5iGgjIliuC1K$sQH~s}qnL=8Ex!7w$l2G`>)6a5|7X8Xlje^W5T@ZPs3B_5}*^aYO zTkS}rt$?tq=j?G0ta@OXcx?Vz+FFeSxpxkQW<=OYRQCMa9Z>~8?wGdUo}UtDO$lC` z`zk=#BJbWKGxgQd=v!UK3|je9Yikh`D7WXhvXf|f;ypY9AcK2U@_Xs&-N25Oq4@Dx z3WaIlXMFNF<4gvwccq!)s+n)P7mPJ2&g_1aL7yo~pClaL)0l2Kyws5Qb9B9`lSg%N zCHIB0G0)TNFel$9igSTXpHvk_1CT`&-}&v5EAo1c+tAlv4jWY?vjw&59X%&f+sBuy z*IlW*J7yj0EE?P&o?~+X@cX0Z_mAPNYSNA}=UhueMjma>BDJLB@sJ&VK^d7+ZXL1j zl?`k+$JJ3@B2cB^Wp02ucUkS7$WSGRwsc9nDlJT5pl)=-KlJpNYf*sl`_!9_`H~Pf zi~DI$<7$fC$-Cz@jP3p0>(d}m!h)cZ@Scj{YEh}gC@h|GJ`yW{i3p?NZzto z>*be#7YS+V$V8{dc@=|7w|xhr<}&8SP$vkfe3Aj|?!Fc%W;~`C-V&-rn-4jl&06NT zv5smiU~7@pcGH99-kCO5IZH-XMGhrGx7R03$M=NUt4a9!``=eC;0)?tyy}=8TR(jL zQVz@k)L}*qr9K`eiiA371*Nv%@zzaqM2H|hi~Fj9dhCb#UHJUyt6arQ;|Ol#ZT|N{ zm4a>aaPMZQtH%kAfaVou<3I+v-7A&e7%w`Da5;514Q}tcH*MD}>E}FWKJ3{w zy8*gV2a&3~$joJbraXBykU4C5L}&_nYC;sMN3b3;%RcmiuKFm||In5mdE*`^t=dOQ zGT*@8?gF`YP*e8ZSXpt=`v5<7}F<bw&l&;Wz{&<6;uSph`Dx>h<1gRl4M9$pZ<1~0^*e~__ZSjrV5#rnOJjYI zup@W;vP$5A$>4R79HDADB4LvDhp9}`uxX35Y`1c&^D}%IkDD!9_U?Zi|5Cv6_L0Mk zW=FO29zd`-c-Mxm`qqhQ1gwJUKqJB8LJb2=%Fm62b*0I@Q^_#)Z6ag4OZz?5D>+{a zUC0z!Z;thx=9CQ+-{00N6Vh%hard&5e`=*qo*F)u^}DTP=i0oi^;rRNGt1C7Ur(7Y zOJnI~pA3__k+;OZfwETX%;)@k6&eFv8qpiqK@`QM8a<#`mY*0^ zF??ZQwK~g|uMMmftfy5@CG!`=;Ki)p3&X?Eilg3rk3tPgRsHOWEk)Xfu^Jr=9$a5RRTxrbq@QgH*cN@1jO%bcZM1Wa4u8 zw0po!{r9iz;qZj2?FUk7750Cyc|CQlK?RAzO&wyutB=fq+k|7cE#KXrBfAx~G{ zDA3+W8mhW-yL6eh1|}|M=oVtpWTr?8e%hQ!<_$KNCfk$ci)==A8QQ|3QSm|%5$)CZ zdDEmSwsW)$LrR8t!nVMaYs$5NnT_6_h)1><4%amaJ#y7Kp|O!ujH8DP-3v^CM%iKp zPC!@O^tGUE66lNv*U#)p)hk&;y^=a&6kF}WR#$5KhA0}5+LV$#GUTfHViLb3>ndB6 zBcz;Ca>}Lvb-sx{hQ3~-tGFSD{F|SLuj55ql$F|@MRPAimCZLqa#1aWZrgsJp;Aw1 zc1&PLq5N5s#b-vdh{EC`ND!u@$$(L=dH{rFH-$|3d~mbh8$;)I%442jMrg5AAAR)1 zs~EfXfl?7%@9YB%jWy-UOn4@DGVZ+bv)Agkp4GwwH} z#txZ#|CJKY3W$pSln^X=Zg#ah>YZ(ma-EFuPWszSDP;A2?N>-Q#^9SYED747U`I>X zf3*lOe>7^?Tl0%aeDUrzBDU>!?;{g>#{8DP*w^WXUwvsG2MK4z#_sYY)?EtvMzXt} zH3w;U<(^Ik7ne`(H`r;SZAoFR_WPXelf=e-zkZ2|JlJSYjIf1KLwJ^YRW$weseDd& zRUM~S`@-JC1m#V-4aI@Wi#lxi6vii$^4tCOmPRr+4S~sX3O+@B#_{Tre}si_<#&`t zNuQ$(MO%U;Q(LXt+VOKNwORu10Ml-?EB>E{waMad0F@; z?GN+i21}u$w05AK3E*DJ6V5kmbP9dM-QB2tu1{+~#KQ&XkRtD$NlyfSAldR6DLUcL<478PIu1%aoSBJZ30* zbj1V7?bJp!=czZDz7e;vjcyL51~gWe7Rr(07wCCxh$uw+nBgO8FS@UwWJSQ}e zz2`zs+i+s&YC^Mt8jcT$7r89QyTONVt>?RHVfVJ%c*S|%#UTgCB{Oak?oNen=;azD1I2=v}Dfmb9eSvq5%* z->%R30BQ~dTMf8)wOUs0NjZ2Yj*}Uc-_>0~gidLudrfk@7G>-5bJlwON^@@ZA&MX* z?GpEw|8}r>bh+MxK%J=OgeZEUmYdPg3qYfd{4R6nyNwo8*F3UvGhTPpMuqxk9b7vV zm0Z1FQ-m=1$h$3E_R1y7KRu6cUoRYtN+NP@t+1bUO#KLqRgD{ zt=ya_gqU{lsfS+wf$;|GTF|AUpk3Snu|`Dhk*N0MOPR<%za7wFk3CG!EMsdhR2Xb)nu)D%9Uac_{3hbjXb z(U$h^=Zr0Im5!i&4CcuYvyOveCuWKd>W67lkYADOo}Z(AvuY!M4b~3#J`#PAHd|~p zu(($Fz7F-66OB|$$0vk>)*;X++*QI|iX2?aLxNh_kx&v~3(j)EVt?@Ujz~$8_7l@^ z`<#{QJr*k&tOb;6M@rIxeNt27AkiycuoyF)n`gY>F=k593$PeqF-`c!;yr8%rr!k+ zB3Q?Z^MCbm^Te9yk7|t%>1O#TUT{Whp;rG=*cGmm%(TcpEB)W;7~Txo@S9qFxMUKE zUuF_v?aGQEm~T1}2$zdpw3|`ICO^!klWQ!N)SUAq%vz57ih&yGXt$LA;Ruw(*fJzG zX`s-aTM0lGr9+14H%$wA2DZ0hHjCM1{X6=5juYd~->^!lkU?n>5DJw1`5Nl`L?z=s zh*HOwtpo?TqMQT~8mnmnH3C(A2_ zx1H0;rL;_BT}F*fCg7(-AH`C8+@m4bZ$E?tC4!tnW-ySo3!3%MAr}GKsqGJMdKSHI ze>$aEzZJbty1r&g-~Vgbsl8Jjp~`+Q)X@2xgjJj}o(p~Ke@QiOqUZ_!bXL`#q!6OT z%IwsE8%I6!BYUGp8ify}y+yoI6wg3+6@yqXFP3DaW^?sUrDa6?8&CbNTN}- zJPliAZ8xiaKFUVtI&@F!USlQOJ+G8y#5s>e(`}Q?d$wLtlnWB)3ofRja!$_%`Q3ky zD+Q6dHQ$?&=Mu27b8PZpmTAjW+{jx@Zm zSaEpeN((8hqmlYcJ{_jtnm?vCz+@!RLfn-dTpRsHFQRAY4q3IKMRFX8E7+LIL+`R1 z%ss5PdK!_nMUFERsf!w#0*lIZI7+IllttXc{wy}lUe70eFz)f>*yhj6eNmAaES?=< z0nGW1JTex3KkC)LPJ!E}f6X>Rb~}5=-!Q}-q!lPrtfppV2Oull{HCv116{$Ec0SA| z0|pxXU56oA|HMpbsYxA5D{sQQN>2bkQR7^j{9tII&&71aFy@c7FE5VrlC4KI((xPN z>4928;Ud!tb8{NEKx_r$Huy8_GWVy}r>V`t9LmzAa%3v*_AacT@!qwZVSyZu#Y}+HJc%BRVsP7TduO<~=L8fl675L{c zfm5`(>*$@`zr{;5iZ&F1Jl#8P;S@aM@0c`*H1*?A*J3jAx9TwzlgTN)R=v zv&!&6*51mw)6Y_FvzIV%9`#uJ4kOQb0FfN)U0mT3qH_dnGM8>xDv{!}rjRr^LgyjK z5@ye{g{sM1J1Im@^xv9Qy%Foa3R8xIULkzsL@otfT%TpcR#$g@ zZHfBVXq)!l?0U_FE!hxvx4QabVi_XcNmsB>0Wa>Fl+73IQt2~OPNlI`mRS$Yucjgr zfZYoYY9TmDlJ{hE=`YjUIVUo78~o-O88K{#dwXFmIVAs<9;qjSI};#VRZHbG4aSC2 zSqqL)IX+S!CO>}&-IFCK2sTo?7jf1j57E_sj+t?Zv8{a~?&@994*^Dfy?whJXZJ5n z^^bku<6aZg{S2g&@Ykzah8mcOf)|gbE~Vp zbyR$5f&#sQBI6U9rUi$RSEFThNWUG#$JK95Ca-Go2xl?+OwMF@Ui*`re=KAgR+*ZC zcJ%}F@X6>f4JDjdRSWXj`TmM^@sygFT^1i82=*OeOvJp0iF^(4ZA-BBI#o&(x6Ec;&jr5Wmqn!Owq{ElJ7mh9Ei)PE2@Ro2Yb{2qC$JgpE zgz-UZaYEudSz_HP_GRhZXZoYb=tWX29D9m88*dN}t1^c3P{vNf+OCzkBzV8RjJIQ( zf_!VnA#C_T#!fZJ2(6D1b?7A-0cF8u{=^+fs3}8NW=b)oB{8%vU)72lLZ}futBuWa zc}~C*v_Vg3PKZKj$Fr#zvxJETYp-D<)H<@wM@T_A;P-`rzcbdB7g;j>lIzU|-8;Xx zhJJTXUQ_-RCh0fc82bRP9Ol?ef4)1}(aiESP=WOw!pyLeiPKXPgQ`hAjWpGy#w+xQ zg-dQBjAVljk}%wG-2Mhp<)0bPO0xC|FmR@3UM;y^fC8dMmeYE|2iKEfG!`r)a!*QP zuKdDcgUpCXzIi;G^${VRy?{L-z4!BBZr(9Kt&NeBZ)~zY)2;RkP_OtEmivtV=u3n{sD8ZTql{0}vpP z?_*L7i|~-AV9oEB2#-eRnt~!-PCPUTd5}l2Q8qLDPNz-8g#tpZSl}*#47y@)kz6xK6jnGVUc&Qba*kwIVI)L!TBas86zS&Cbpm>{cVAIKZtl_ zOQ0{t8&_gQSZ-*W@KkdgkOnwj5a2Tz-$cJNFAfUft|Lc2nF0?`LNk!A^V~255xj4_ zfnpxg8tv7uWISi8M0xig50|{}oX6g*8;>S`{9+Cn+GUg5x853yTgiVE5>dU1n3OAc ziqDQ68m*A>%4_vG5@idkCZzp`5lx3}`q$$AA>20xUW{yY%Z5i!LMr&9MXj!o?80i$ zG6?2Eo^huv^(s0I7=DC)BRNLjM>#X}^X@I~ zxNwuEiXhN-`*}6Zea2;f^0x&3gb7MrLdsYt-Bf~!zEC$7HpD3)gUbOAGAv`ag1?{x z_=bM;rs7n9cu~8#zs6&3dr9=KLmf0~&rIkclhvCH=tXkz1n3#9X9x@9Umsq0(@v zWMA{4rQ*8D$NuNDml&967Qx@GK1%cT#^#~{_j_Arq$N9Vxz=&sRSsb4tkw>{LWl_L zGWr*^!u0sIuJ=jRymwvS&$C$7b%$DngwB{}1b=He9O3;}t?s>}i3;xG0*8>;tJs2V zC|EciSBW^ai{052tKLmgHx|(_DgLC)Rl)%n`Pk-*o}t-8fBb}sC$$(dyO^Swp5hae zE~judpR?xsh^@<1QWW=6U|_It>~d?o(JIAyPgjcgu`!&xkCK}6S~cTZ%;>3YrCrj&hOBvx+t#0icNG>Q`4eyE9aL4bPSkR2iGg5lZ`@6c z2kLpAW`gdEP}LA|`+w4ximc)CP*+pn^(F(g&mMs+)b+-w!uwafYW+TyFR$lA!ipHJ zxKHfFyRpNOkwu$mu-~;e6&P4ui?QT~#w*&mhD}-@h^Z!%MtA5k+$T48dEf%5-N*6P ze?L-XkAFEyIHQz4QuS)SmXROylmEmm4dooNLGfmsHv; zDso`uQG#ed`n`k=f;Ns_EA*d35y<2$#?}CvQd`a;J!CiJ=;9b z2mdj#ILoB1Z!>~YC!)c2q^Osqyz=gr(e5W)Fv zM-Au=VT!b|l2$l*!@X@rM625Bo&mex+5KI4p(KfqNasJc`U`5SCF#k-E9LX3F1WHN zSXIMEYRtqN7h_$wymU_8_b9!xiL}oK+$Kh>b&*DcOi%7jZQDr6ibC{UPuZY7jm&eC zx39loQXuA2!lqq<%^ZF$a{MCd5LQ!sArlkP*S7ekbi+-|q6AS+gNA&>!hGY{rOA|8 zAFnjsEE%ks{KMALIahkjdJ|c8f5g;HEgYhE&iA`tW*y(L-))?cNhy=Tsdx{BTl~el zhfDs;ns(mEyM=5#Mt{st059lDEVR=iWk2=StAWcvDmK{u24MC})00cYIl?sx=-5c| z9la;u&U;-(dh$lpjR;q3{SFDq$&cgzfBvPN89B@alR^Tm3e0D%yESE_3fcm1OMJe> zh*-J#9Pr|0B*n}vf^n9&T+~WLO@#8$GKdpz4+>R`r2a}4Juy`7DT-Xa<+|P)dYB#9 z@T+X#l&mM_jcZBQfX~)ZOVIW8X+JL8__mgiM5KjAqM5Dy73H3j)X2VP=j9H)Ko*v` z(VfWMQL)a#`ncgx;lOS==S%P21V0~_v%#uhwKF0O!WX9s*+pLIEip7=-k!uYmRSiO?;wgBhws>Ohik6P(#6Iu4yksB zy^I%>y4AQJ18tVQS#&!zj6RS?V>zURqaeb! zR84co->$K%U;&fwS5Ft**0T~FdIySjMz!d@B%d!Zn=EW-u&Y3yu7FXCJ3Z8VcvrlO zmeX~i*`BY%ESM9 z$wxjj_3<1WAjvW!oeO3bw8;;B8LhiBb=s~}2$j(etq$uUd5%QN`sQ&TZW}v|>Wj^l zq_=)w-0CSYa?bFuXyBGY#KklKV@2M!UG}>Ll zulUQ~TJB2E1f#ygRv^-RLUQ(MK=VCCzO^RY=voL-Xi*-DdYt9!az*~r?Et?o8MP7x zpmF=Zzf=NedM->LCmr~9{>RplCbOTP=J$ziL5HDSeOg2;Cg%aF8uz0T(&)qKrDwUD1qye^}iO7?Q*Svsv<|w zCu*8I49Yf{XzX(+hzPN6Zf-O&xw`u@~JVl^75*Ca*Lw(islun zY34F?hwXtfdEO(}6J43!y~f@WSOmHHQXYONs>5`zC=D2jqBRzPTLiUt!BLM1RiS^2 zhq2JzQeZ0uwv`FwmJ;=NQyf_v=>QYtN3`88xeil1|)gXAVNyx-^?5j|N8-lra?I7 zRG{ynsEfD7tJN0p2X3sEYvxi4UIH7cK$$WWaGl+kUaFI;#YSO&U~`aick|dZbR{zR zlJ+(Y^q5)iw=s&$Q=7%hpX+FdreKHO?6R++=6we$o^(Bs}!&r zD#y46y|(bBVk1Yr&!q@lSJCJgEfLY4k5>Ekb0j;&EioAL3h9Wcqrlc3hUpB4YVm3H zyfl9U^p)OU;bSSE_h}mu=YreZ2cJtJSJB_@F69xv>YHW6j>~0zfZb>?Hd2Yq$@yKL zQLEhuQ}CYF`I-{hzq0 z6q|~wc+9JFh^QuZL1yMq*Dy&$kCt&s?`siDuYWyX|gaUD~Kec1c5eQ`v`k zvhT6KMt=SWwhf(=#FEJnHq!yTODk{AZO)%;bL|axWK5MaQAENoclbd; zsdPEwXL9nQv3}W^%ft3h5-AR)Yb9=~X9cTZeiZtxi{dY4?*4$xu}FLib|_V2e8+_{mX{Op5fbD2H3Vy@z)0Vrjp52cqW zO|fmXNHG^zVM#PuJ{gg?Hy#r>MN%Y+UA%i(*@OzYB6J*uMH%EFSMjq?2pMMhitXS3?-pYohEq3Wduk0Is$syOdW?DXh|UWF&_ zcNL9*DnTZGuP!`?_0-Y}pImD=1m#r|iA*=Ioeo(j+$K6`aFwX85qBq?f0=%CZ_y;-yO z_5$*z!my3j{Cxly)kO35ybErSQTxF8EMIm5u*c}L-}jFyz|_;|!c)0L37vbBWyG^W zr|No4`zno|rCeK3WLBjo8v*yhB+HRtJ5xg;v8ajp((@}d->fOF#S-eU%~S5Jg#r{s zsVU3;s(eP|U?_nWZ=j^Fv{P_HfGBq2f5C0ySl$?O2 zys^7UuP?f{mKiVn!9&-_^EBU^N2m5cjyOtSBV~%xb^kVFj=~LGa`9w0VhGm|V-TO( zT=?{v8*xipo=KGJ?EQELEvwPecvD;TdSqb2neqMePebt|ODX&EZ(&27fpK$AE55U* z5Q{HUl<9{P3XclkV9!X+N#2*h)(b?s>Z`#AMQ*ywk)SS(`UQXT)j2-o zc2^>jo>d#R4XZlk4nO27nzAE-PHfq+AY=psPf&C-GJ;jmYYauqa&=zv;I@|9H{9qO z75;7*jwmhI(rpgNFw-07N9?RhTCK*LR)l|*3|V%LW1)?Dy9MbGWr!VGweeW5ULWp} zux$6KSl}i_!5jMY&Zw&>Ch={_DUw$+_vhKY1argENi)&Dme3og#^!hPhcAbxcjTFV zs3^94$l{1dqGx45uedd>_Qy{55Xr2_9sOs=R;&9X(p?Mp0?L;E>`e__J>{~wFlkty z;bfIb#fZ?YIzx;J4^~)7@u~Ep)9()k#l@=~M~$!=ldlm}Wy-m0h2Cd#J}jqX4@S(_ zqagfThe};GOPk3gXG8@WG{@K#k__rPBqC%PvTCE5)oP_bk*l_PqBJPpLwntgHeU~& zTI613a@cU!4TVmXu~$T99SyBu(lQXhH*z`;{JrO0mj34*B%Eqi6g|+M;Sw~(Of2_DuxT4Gts{kB(@eT1thap154|`QWSa|K#Tfo!^Y$lA zT(hmP1WNmU#|0&cC;EL4*`$HCtdl~-dk&P|r+~12i`Sr1@;uQ;#$^4JaLS!caN<#o zx_2}zNIqJx(t;kvwYtYo7Hzf$gXQkm;p;^Y7<2hqYw$1J8g@nhUg~=T@*H$^J+}U< zW4#OtE3A3)^&KkQO+`H02D&EDTj3Z7#+|b*lNK;4aby@uGv%em!O);lREbkdsOrgc z$OkR{rlZPr2A$s)Y+Ee&ZIyiSM&ICFp@XZrM>BsYH2Z1sS5}mFfbbC-Y)?FX4Nn1f zmw+0_$=s6;NQvDN17<&sch@bip@a(ir$zh65c$1J3;e4nqCe3X z-=`n))t%sL#r_tcv*CLmcau_Nqy0V;gcu%p=aWls!9E^Ppl373J}c|FR3zT9D=_B{ z7<(O2Mz*KO6rsMMHQ!Wh-M9I4Pf-Moe2(aPEz!yM?*o$X33DdqTHT)9lsV;gP=Ta`08i3`W2vpi@_|EEhGwc+x2?kSBaY!dR{v(a+vHfJo4e zzJd6RRzK(A^KhEmV$Abd@44hEV`QkwH}m!&5}$3+vxu8hzm`(SOWJ=}vD_MVVvT^A zPnm(r{`l-Omg?pM1e@}ck2f3cPhPZEB(Bv!#a{}ZBdk`M68dTDuIu7dE-0MyvaT~0 zWKYY#Uj3}zk_u-Fw<#}v3D9XD;FR!uK3yO$_}x}gxV9H)->(=D%(l?DJ~%!0I#tKH zfAOxF0xf?Khgv>jYj{)DkFd8}S4BQ`Qu&`C_pAnMu(s!T*{Rf1N$GrCN^w*C=YMm_OSM{i*9-Wre!LQWVx+{Z%`9F98eEy~ z(Kb;(LtxD|(6c}1BY)fJ;3pGJhx&Hq%zj8J>0uvjFEq`o(#eeS4f>d&tXyEF$DDu9 zMvQELa`z^y2J=O!jrZ<9`!Epsr$g2q!t>4k1}BHqTJ5c^sKot5J}vK&##-IZob=6C zvQken=2@hIFyrbpgrNf8Tg1ZI(<#-$y*{3d^eEk;bf&qy_Sx?NliSJD3rzX#FNaaZ zl`JY5o7-D4x!=xP--$wdD+U~1=TF80&@$>7U=^gp)%nqpWQ zOIsjz4|m2;R>ASitUaunxkP3O^4ZXJCK+~eP4hB7K}BVAm~>h5}d}~^W&*?rqz;>f7Xo9U*?;9FF*Yj$>yd|qt;ve7m)aRLBan? z;2GJOnCVpx|4?d#b>uFTC!1t>w;eTg(#a&!TMp~GG!F(Pk{N96r#x4$t2IKODD4AP$0wrJsr-ON&xN9~q3p zHsD)0sU7J%5@Ugc!mA>Wm!Avvq|$DOC`;0IN{~y3Zrp#K`=+<|PqmnJm8b@V0%}BT zzMau_48LNM6gSGWbYq>gO8%tIL0&B_^tvjYcT3`{GCli->5$PD^;|ZZBPYOPsxb8>j4w2mXOD@c7Tf$BoS=ERdXzgxs zAQz?r3?KYHSyDZ}2Up?V5+)RK%@4?HTb-60Bk?0!PPtvLC(h`Q^cstWBpnJfiXm&N zB{TRRzrpl%h!A@#IB#R;oxW>N$xfBSE9Q-p2at8SN2$%HZt^~b?4b-OX6a@be82W* zSTY38i)a~mqXzFqnXBo%H{)B2F4?}7AuDj_Y`4*3$Q91#Lv;fY1#+3(rYvS!K0mfg z8!qcfeOekYASn+J(JI$#pr$X6AoLeh$zc_PSH4AepZw*WvGQ*^jq#dLUHtgo+48N< zhE?=baMlI!A|PAAVaPUF?ff2lib}5vBtj@AC=$wG zh^PocA~jT5RC*B%B~j@ufkdPS5v3y<2wlP~Ad(P@5Q;Id2c6md1iOFv3C?@2>%8SW z@9()Y^S7UhI@aYF<>WOCEBSq4R~q9Q8o@=TS>K#DhHnSp`dQ4u`W);kjxmsk|HzDR zP2X6c*GIG*b;FNp?{+XLUW1(uZk)GJKGYQ(Qx%^Ou2NZscR8hV=hGeO)j}7BR9-I( zp%*T6{2Yrf6=<1J%+b5+Wz}%li93nvMGVHkRlrOW4~&3|+Ql>fEgUkkMh? zq#84Y-yiebh}IVNR%0A*(F3k+!}mmLrsC@HKiX|M7V6* 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 literal 0 HcmV?d00001 diff --git a/doc/specs/#1043 - Set the initial position of the Terminal/images/ProfileSnapshot.png b/doc/specs/#1043 - Set the initial position of the Terminal/images/ProfileSnapshot.png new file mode 100644 index 0000000000000000000000000000000000000000..ce4c5b47ebb7c9acc1b1b4ec1f28ea4345ff6253 GIT binary patch literal 26198 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 Date: Tue, 3 Sep 2019 16:34:08 -0700 Subject: [PATCH 02/25] Fix the spec title --- .../#1043 - Set the initial position of the Terminal/spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/#1043 - Set the initial position of the Terminal/spec.md b/doc/specs/#1043 - Set the initial position of the Terminal/spec.md index d1fb85ddc21..79cf4ca4982 100644 --- a/doc/specs/#1043 - Set the initial position of the Terminal/spec.md +++ b/doc/specs/#1043 - Set the initial position of the Terminal/spec.md @@ -5,7 +5,7 @@ last updated: 2019-09-03 issue id: #1043 --- -# Spec Title +# Set the initial position for terminal ## Abstract From 5a919c7af46baef97c48b75fd66bf966c0b83ef0 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Wed, 4 Sep 2019 10:43:58 -0700 Subject: [PATCH 03/25] Add a new function to read initial position properties --- src/cascadia/TerminalApp/App.cpp | 13 +++++++++++++ src/cascadia/TerminalApp/App.h | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 6 ++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 439a6367907..38b99926ca8 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -458,6 +458,19 @@ namespace winrt::TerminalApp::implementation return TermControl::GetProposedDimensions(settings, dpi); } + COORD App::GetLaunchInitialPositions() + { + if (!_loadedInitialSettings) + { + // Load settings if we haven't already + LoadSettings(); + } + + TerminalSettings settings = _settings->MakeSettings(std::nullopt); + + return { gsl::narrow(0), gsl::narrow(0) }; + } + bool App::GetShowTabsInTitlebar() { if (!_loadedInitialSettings) diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 47350c76056..88972553d4c 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -32,6 +32,7 @@ namespace winrt::TerminalApp::implementation void LoadSettings(); Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi); + COORD GetLaunchInitialPositions(); bool GetShowTabsInTitlebar(); ~App() = default; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index e2a72ce5594..0952eac2c5c 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -181,8 +181,10 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect) const auto adjustedHeight = nonClient.bottom - nonClient.top; const auto adjustedWidth = nonClient.right - nonClient.left; - const COORD origin{ gsl::narrow(proposedRect.left), - gsl::narrow(proposedRect.top) }; + /*const COORD origin{ gsl::narrow(proposedRect.left), + gsl::narrow(proposedRect.top) };*/ + const COORD origin{ gsl::narrow(2500), + gsl::narrow(100) }; const COORD dimensions{ Utils::ClampToShortMax(adjustedWidth, 1), Utils::ClampToShortMax(adjustedHeight, 1) }; From 5e947ac9bba357146a3e958f1c669f4bc0da9b40 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Fri, 6 Sep 2019 10:54:11 -0700 Subject: [PATCH 04/25] Test version - 9-6 --- src/cascadia/TerminalApp/App.cpp | 8 ++++++-- src/cascadia/TerminalApp/App.h | 2 +- src/cascadia/TerminalApp/App.idl | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 10 ++++++---- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 31e4a387128..e77c829252b 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -338,7 +338,7 @@ namespace winrt::TerminalApp::implementation return TermControl::GetProposedDimensions(settings, dpi); } - COORD App::GetLaunchInitialPositions() + winrt::Windows::Foundation::Point App::GetLaunchInitialPositions() { if (!_loadedInitialSettings) { @@ -348,7 +348,11 @@ namespace winrt::TerminalApp::implementation TerminalSettings settings = _settings->MakeSettings(std::nullopt); - return { gsl::narrow(0), gsl::narrow(0) }; + winrt::Windows::Foundation::Point point; + point.X = 1000; + point.Y = 800; + + return point; } bool App::GetShowTabsInTitlebar() diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 9005120d8f2..90f56b88417 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -31,7 +31,7 @@ namespace winrt::TerminalApp::implementation void LoadSettings(); Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi); - COORD GetLaunchInitialPositions(); + winrt::Windows::Foundation::Point GetLaunchInitialPositions(); bool GetShowTabsInTitlebar(); Windows::UI::Xaml::UIElement GetRoot() noexcept; diff --git a/src/cascadia/TerminalApp/App.idl b/src/cascadia/TerminalApp/App.idl index 9e8981b1d33..4b20360059e 100644 --- a/src/cascadia/TerminalApp/App.idl +++ b/src/cascadia/TerminalApp/App.idl @@ -24,6 +24,7 @@ namespace TerminalApp String Title { get; }; Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi); + Windows.Foundation.Point GetLaunchInitialPositions(); Boolean GetShowTabsInTitlebar(); void TitlebarClicked(); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 2819f54ebb6..0c08ddeb424 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -141,6 +141,9 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect) auto initialSize = _app.GetLaunchDimensions(dpix); + bool useDefaultInitialPos = false; + winrt::Windows::Foundation::Point initialPosition = _app.GetLaunchInitialPositions(); + const short _currentWidth = Utils::ClampToShortMax( static_cast(ceil(initialSize.X)), 1); const short _currentHeight = Utils::ClampToShortMax( @@ -183,10 +186,9 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect) const auto adjustedHeight = nonClient.bottom - nonClient.top; const auto adjustedWidth = nonClient.right - nonClient.left; - /*const COORD origin{ gsl::narrow(proposedRect.left), - gsl::narrow(proposedRect.top) };*/ - const COORD origin{ gsl::narrow(2500), - gsl::narrow(100) }; + const COORD origin{ gsl::narrow(useDefaultInitialPos ? proposedRect.left : initialPosition.X), + gsl::narrow(useDefaultInitialPos ? proposedRect.top : initialPosition.Y) }; + const COORD dimensions{ Utils::ClampToShortMax(adjustedWidth, 1), Utils::ClampToShortMax(adjustedHeight, 1) }; From 8fbe307db22e5a44e5fa7ee589b05d1df6aa934d Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Fri, 6 Sep 2019 16:15:06 -0700 Subject: [PATCH 05/25] spec for code review updates --- .../images/Image1.png | Bin 22238 -> 0 bytes .../images/ProfileSnapshot.png | Bin 26198 -> 7153 bytes .../spec.md | 27 ++++++++++++------ 3 files changed, 18 insertions(+), 9 deletions(-) delete mode 100644 doc/specs/#1043 - Set the initial position of the Terminal/images/Image1.png diff --git a/doc/specs/#1043 - Set the initial position of the Terminal/images/Image1.png b/doc/specs/#1043 - Set the initial position of the Terminal/images/Image1.png deleted file mode 100644 index df249ca4567e39b6c2f345356d51900eb198fbed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22238 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%0A*(F3k+!}mmLrsC@HKiX|M7V6* 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 diff --git a/doc/specs/#1043 - Set the initial position of the Terminal/images/ProfileSnapshot.png b/doc/specs/#1043 - Set the initial position of the Terminal/images/ProfileSnapshot.png index ce4c5b47ebb7c9acc1b1b4ec1f28ea4345ff6253..c73633a3855f75e20ed71433abe1e182c08a0ba6 100644 GIT binary patch literal 7153 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 literal 26198 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 Date: Wed, 11 Sep 2019 18:33:10 -0700 Subject: [PATCH 06/25] Code change 9-11 --- src/cascadia/TerminalApp/App.cpp | 11 ++- src/cascadia/TerminalApp/App.h | 2 +- src/cascadia/TerminalApp/App.idl | 2 +- .../TerminalApp/GlobalAppSettings.cpp | 73 ++++++++++++++++++- src/cascadia/TerminalApp/GlobalAppSettings.h | 18 +++++ src/cascadia/WindowsTerminal/AppHost.cpp | 14 ++-- src/cascadia/WindowsTerminal/AppHost.h | 2 +- 7 files changed, 106 insertions(+), 16 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index e77c829252b..bc544e5fa1d 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -338,7 +338,7 @@ namespace winrt::TerminalApp::implementation return TermControl::GetProposedDimensions(settings, dpi); } - winrt::Windows::Foundation::Point App::GetLaunchInitialPositions() + winrt::Windows::Foundation::Point App::GetLaunchInitialPositions(const uint64_t defaultInitialX, const uint64_t defaultInitialY) { if (!_loadedInitialSettings) { @@ -346,12 +346,11 @@ namespace winrt::TerminalApp::implementation LoadSettings(); } - TerminalSettings settings = _settings->MakeSettings(std::nullopt); - winrt::Windows::Foundation::Point point; - point.X = 1000; - point.Y = 800; - + point.X = gsl::narrow_cast(_settings->GlobalSettings().GetUseDefaultInitialX() ? defaultInitialX : + _settings->GlobalSettings().GetInitialX()); + point.Y = gsl::narrow_cast(_settings->GlobalSettings().GetUseDefaultInitialY() ? defaultInitialY : + _settings->GlobalSettings().GetInitialY()); return point; } diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 90f56b88417..4e3b45ea5ef 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -31,7 +31,7 @@ namespace winrt::TerminalApp::implementation void LoadSettings(); Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi); - winrt::Windows::Foundation::Point GetLaunchInitialPositions(); + winrt::Windows::Foundation::Point GetLaunchInitialPositions(const uint64_t defaultInitialX, const uint64_t defaultInitialY); bool GetShowTabsInTitlebar(); Windows::UI::Xaml::UIElement GetRoot() noexcept; diff --git a/src/cascadia/TerminalApp/App.idl b/src/cascadia/TerminalApp/App.idl index 4b20360059e..b0990acf1b8 100644 --- a/src/cascadia/TerminalApp/App.idl +++ b/src/cascadia/TerminalApp/App.idl @@ -24,7 +24,7 @@ namespace TerminalApp String Title { get; }; Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi); - Windows.Foundation.Point GetLaunchInitialPositions(); + Windows.Foundation.Point GetLaunchInitialPositions(UInt64 defaultInitialX, UInt64 defaultInitialY); Boolean GetShowTabsInTitlebar(); void TitlebarClicked(); diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index eba5beedca7..949dc50bc96 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +#include #include "pch.h" #include "GlobalAppSettings.h" #include "../../types/inc/Utils.hpp" @@ -20,12 +21,18 @@ static constexpr std::string_view DefaultProfileKey{ "defaultProfile" }; static constexpr std::string_view AlwaysShowTabsKey{ "alwaysShowTabs" }; static constexpr std::string_view InitialRowsKey{ "initialRows" }; static constexpr std::string_view InitialColsKey{ "initialCols" }; + +static constexpr std::string_view InitialXKey{ "initialX" }; +static constexpr std::string_view InitialYKey{ "initialY" }; + static constexpr std::string_view ShowTitleInTitlebarKey{ "showTerminalTitleInTitlebar" }; static constexpr std::string_view RequestedThemeKey{ "requestedTheme" }; static constexpr std::string_view ShowTabsInTitlebarKey{ "showTabsInTitlebar" }; static constexpr std::string_view WordDelimitersKey{ "wordDelimiters" }; static constexpr std::string_view CopyOnSelectKey{ "copyOnSelect" }; +static constexpr std::string_view LaunchModeKey{ "launchMode" }; + static constexpr std::wstring_view LightThemeValue{ L"light" }; static constexpr std::wstring_view DarkThemeValue{ L"dark" }; static constexpr std::wstring_view SystemThemeValue{ L"system" }; @@ -37,11 +44,18 @@ GlobalAppSettings::GlobalAppSettings() : _alwaysShowTabs{ true }, _initialRows{ DEFAULT_ROWS }, _initialCols{ DEFAULT_COLS }, + + _initialX{ 0 }, + _initialY{ 0 }, + _useDefaultInitialX{ true }, + _useDefaultInitialY{ true }, + _showTitleInTitlebar{ true }, _showTabsInTitlebar{ true }, _requestedTheme{ ElementTheme::Default }, _wordDelimiters{ DEFAULT_WORD_DELIMITERS }, - _copyOnSelect{ false } + _copyOnSelect{ false }, + _launchMode{ L"default" } { } @@ -129,6 +143,15 @@ void GlobalAppSettings::SetCopyOnSelect(const bool copyOnSelect) noexcept _copyOnSelect = copyOnSelect; } +std::wstring GlobalAppSettings::GetLaunchMode() const noexcept +{ + return _launchMode; +} +void GlobalAppSettings::SetLaunchMode(const std::wstring launchMode) +{ + _launchMode = launchMode; +} + #pragma region ExperimentalSettings bool GlobalAppSettings::GetShowTabsInTitlebar() const noexcept { @@ -139,6 +162,32 @@ void GlobalAppSettings::SetShowTabsInTitlebar(const bool showTabsInTitlebar) noe { _showTabsInTitlebar = showTabsInTitlebar; } + +int32_t GlobalAppSettings::GetInitialX() const noexcept +{ + return _initialX; +} +void GlobalAppSettings::SetInitialX(const int32_t initialX) noexcept +{ + _initialX = initialX; +} +bool GlobalAppSettings::GetUseDefaultInitialX() const noexcept +{ + return _useDefaultInitialX; +} +int32_t GlobalAppSettings::GetInitialY() const noexcept +{ + return _initialY; +} +void GlobalAppSettings::SetInitialY(const int32_t initialY) noexcept +{ + _initialY = initialY; +} +bool GlobalAppSettings::GetUseDefaultInitialY() const noexcept +{ + return _useDefaultInitialY; +} + #pragma endregion // Method Description: @@ -152,6 +201,7 @@ void GlobalAppSettings::ApplyToSettings(TerminalSettings& settings) const noexce settings.KeyBindings(GetKeybindings()); settings.InitialRows(_initialRows); settings.InitialCols(_initialCols); + settings.WordDelimiters(_wordDelimiters); settings.CopyOnSelect(_copyOnSelect); } @@ -169,11 +219,16 @@ Json::Value GlobalAppSettings::ToJson() const jsonObject[JsonKey(DefaultProfileKey)] = winrt::to_string(Utils::GuidToString(_defaultProfile)); jsonObject[JsonKey(InitialRowsKey)] = _initialRows; jsonObject[JsonKey(InitialColsKey)] = _initialCols; + + jsonObject[JsonKey(InitialXKey)] = _initialX; + jsonObject[JsonKey(InitialYKey)] = _initialY; + jsonObject[JsonKey(AlwaysShowTabsKey)] = _alwaysShowTabs; jsonObject[JsonKey(ShowTitleInTitlebarKey)] = _showTitleInTitlebar; jsonObject[JsonKey(ShowTabsInTitlebarKey)] = _showTabsInTitlebar; jsonObject[JsonKey(WordDelimitersKey)] = winrt::to_string(_wordDelimiters); jsonObject[JsonKey(CopyOnSelectKey)] = _copyOnSelect; + jsonObject[JsonKey(LaunchModeKey)] = winrt::to_string(_launchMode); jsonObject[JsonKey(RequestedThemeKey)] = winrt::to_string(_SerializeTheme(_requestedTheme)); jsonObject[JsonKey(KeybindingsKey)] = AppKeyBindingsSerialization::ToJson(_keybindings); @@ -209,6 +264,17 @@ GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json) result._initialCols = initialCols.asInt(); } + if (auto initialX{ json[JsonKey(InitialXKey)] }) + { + result._useDefaultInitialX = false; + result._initialX = initialX.asInt(); + } + if (auto initialY{ json[JsonKey(InitialYKey)] }) + { + result._useDefaultInitialY = false; + result._initialY = initialY.asInt(); + } + if (auto showTitleInTitlebar{ json[JsonKey(ShowTitleInTitlebarKey)] }) { result._showTitleInTitlebar = showTitleInTitlebar.asBool(); @@ -229,6 +295,11 @@ GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json) result._copyOnSelect = copyOnSelect.asBool(); } + if (auto launchMode{ json[JsonKey(LaunchModeKey)] }) + { + result._launchMode = GetWstringFromJson(launchMode); + } + if (auto requestedTheme{ json[JsonKey(RequestedThemeKey)] }) { result._requestedTheme = _ParseTheme(GetWstringFromJson(requestedTheme)); diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.h b/src/cascadia/TerminalApp/GlobalAppSettings.h index 11dab24ccaa..a369e2eb5ec 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.h +++ b/src/cascadia/TerminalApp/GlobalAppSettings.h @@ -53,6 +53,17 @@ class TerminalApp::GlobalAppSettings final bool GetCopyOnSelect() const noexcept; void SetCopyOnSelect(const bool copyOnSelect) noexcept; + int32_t GetInitialX() const noexcept; + void SetInitialX(const int32_t initialX) noexcept; + bool GetUseDefaultInitialX() const noexcept; + + int32_t GetInitialY() const noexcept; + void SetInitialY(const int32_t initialY) noexcept; + bool GetUseDefaultInitialY() const noexcept; + + std::wstring GetLaunchMode() const noexcept; + void SetLaunchMode(const std::wstring launchMode); + winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme() const noexcept; Json::Value ToJson() const; @@ -69,6 +80,11 @@ class TerminalApp::GlobalAppSettings final int32_t _initialRows; int32_t _initialCols; + int32_t _initialX; + int32_t _initialY; + bool _useDefaultInitialX; + bool _useDefaultInitialY; + bool _showStatusline; bool _alwaysShowTabs; bool _showTitleInTitlebar; @@ -78,6 +94,8 @@ class TerminalApp::GlobalAppSettings final bool _copyOnSelect; winrt::Windows::UI::Xaml::ElementTheme _requestedTheme; + std::wstring _launchMode; + static winrt::Windows::UI::Xaml::ElementTheme _ParseTheme(const std::wstring& themeString) noexcept; static std::wstring_view _SerializeTheme(const winrt::Windows::UI::Xaml::ElementTheme theme) noexcept; }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 0c08ddeb424..16824f87157 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -127,8 +127,13 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& send // to appear on. // Return Value: // - -void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect) +void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) { + // Acquire the actual intial position + winrt::Windows::Foundation::Point initialPosition = _app.GetLaunchInitialPositions(proposedRect.left, proposedRect.top); + proposedRect.left = (long)initialPosition.X; + proposedRect.right = (long)initialPosition.Y; + // Find nearest montitor. HMONITOR hmon = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST); @@ -141,9 +146,6 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect) auto initialSize = _app.GetLaunchDimensions(dpix); - bool useDefaultInitialPos = false; - winrt::Windows::Foundation::Point initialPosition = _app.GetLaunchInitialPositions(); - const short _currentWidth = Utils::ClampToShortMax( static_cast(ceil(initialSize.X)), 1); const short _currentHeight = Utils::ClampToShortMax( @@ -186,8 +188,8 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect) const auto adjustedHeight = nonClient.bottom - nonClient.top; const auto adjustedWidth = nonClient.right - nonClient.left; - const COORD origin{ gsl::narrow(useDefaultInitialPos ? proposedRect.left : initialPosition.X), - gsl::narrow(useDefaultInitialPos ? proposedRect.top : initialPosition.Y) }; + const COORD origin{ gsl::narrow(initialPosition.X), + gsl::narrow(initialPosition.Y) }; const COORD dimensions{ Utils::ClampToShortMax(adjustedWidth, 1), Utils::ClampToShortMax(adjustedHeight, 1) }; diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index a66cd93369c..df747b6bf06 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -24,7 +24,7 @@ class AppHost std::unique_ptr _window; winrt::TerminalApp::App _app; - void _HandleCreateWindow(const HWND hwnd, const RECT proposedRect); + void _HandleCreateWindow(const HWND hwnd, RECT proposedRect); void _UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::UIElement& arg); void _UpdateTheme(const winrt::TerminalApp::App&, From 6c59f375704b127c08ab0db440205ebd80ad53ff Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Thu, 12 Sep 2019 12:06:26 -0700 Subject: [PATCH 07/25] temporary changes 9 12 --- src/cascadia/TerminalApp/App.cpp | 23 +++++++++++++++++++++++ src/cascadia/TerminalApp/App.h | 1 + src/cascadia/TerminalApp/App.idl | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 16 ++++++++++++++++ 4 files changed, 41 insertions(+) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index bc544e5fa1d..f9bc8b766b5 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -338,6 +338,29 @@ namespace winrt::TerminalApp::implementation return TermControl::GetProposedDimensions(settings, dpi); } + // Method Description: + // - + // Arguments: + // - + // Return Value: + // - + std::wstring App::GetLaunchMode() + { + if (!_loadedInitialSettings) + { + // Load settings if we haven't already + LoadSettings(); + } + + return _settings->GlobalSettings().GetLaunchMode(); + } + + // Method Description: + // - + // Arguments: + // - + // Return Value: + // - a point containing the requested initial position in pixels. winrt::Windows::Foundation::Point App::GetLaunchInitialPositions(const uint64_t defaultInitialX, const uint64_t defaultInitialY) { if (!_loadedInitialSettings) diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 4e3b45ea5ef..129d7a56b34 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -32,6 +32,7 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi); winrt::Windows::Foundation::Point GetLaunchInitialPositions(const uint64_t defaultInitialX, const uint64_t defaultInitialY); + std::wstring GetLaunchMode(); bool GetShowTabsInTitlebar(); Windows::UI::Xaml::UIElement GetRoot() noexcept; diff --git a/src/cascadia/TerminalApp/App.idl b/src/cascadia/TerminalApp/App.idl index b0990acf1b8..e41670f8e4c 100644 --- a/src/cascadia/TerminalApp/App.idl +++ b/src/cascadia/TerminalApp/App.idl @@ -25,6 +25,7 @@ namespace TerminalApp Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi); Windows.Foundation.Point GetLaunchInitialPositions(UInt64 defaultInitialX, UInt64 defaultInitialY); + String GetLaunchMode(); Boolean GetShowTabsInTitlebar(); void TitlebarClicked(); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 16824f87157..36b2c98c875 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -134,9 +134,25 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) proposedRect.left = (long)initialPosition.X; proposedRect.right = (long)initialPosition.Y; + std::wstring launchMode = _app.GetLaunchMode(); + // Find nearest montitor. HMONITOR hmon = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST); + if (launchMode == L"maximize") + { + MONITORINFO monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFO); + if (GetMonitorInfoA(hmon, &monitorInfo)) + { + + } + else + { + + } + } + // This API guarantees that dpix and dpiy will be equal, but neither is an // optional parameter so give two UINTs. UINT dpix = USER_DEFAULT_SCREEN_DPI; From e2999b1e87517996ca60cd3be62f49ab91245ef6 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Wed, 18 Sep 2019 17:35:26 -0700 Subject: [PATCH 08/25] Enable setting an initial position for terminal and maximization launch --- .../spec.md | 8 +- src/cascadia/TerminalApp/App.cpp | 35 ++-- src/cascadia/TerminalApp/App.h | 2 +- .../TerminalApp/GlobalAppSettings.cpp | 4 +- src/cascadia/TerminalApp/GlobalAppSettings.h | 6 +- src/cascadia/WindowsTerminal/AppHost.cpp | 150 ++++++++++-------- src/cascadia/WindowsTerminal/AppHost.h | 2 +- src/cascadia/WindowsTerminal/BaseWindow.h | 9 ++ src/cascadia/WindowsTerminal/IslandWindow.cpp | 13 +- src/cascadia/WindowsTerminal/IslandWindow.h | 4 +- src/inc/DefaultSettings.h | 1 + 11 files changed, 146 insertions(+), 88 deletions(-) diff --git a/doc/specs/#1043 - Set the initial position of the Terminal/spec.md b/doc/specs/#1043 - Set the initial position of the Terminal/spec.md index d3cef3a0ce5..2645f6c111f 100644 --- a/doc/specs/#1043 - Set the initial position of the Terminal/spec.md +++ b/doc/specs/#1043 - Set the initial position of the Terminal/spec.md @@ -42,6 +42,12 @@ Step 2 to 6 should be done in `AppHost::_HandleCreateWindow`, which is consisten In step 4, we may need to consider the dpi of the current monitor and multi-monitor scenario when calculating the initial position of the window. +Edge cases: + +1. Multiple monitors. The user should be able to set the initial position to any monitors attached. For the monitors on the left side of the major monitor, the initial position values are negative. +2. If the initial position is larger than the screen resolution and the window is off-screen, we should at least let user be able to see and drag the window back on screen. One solution is to set the initial position to the top left corner of the nearest monitor if the titlebar is off-screen. +3. If the user wants to launch maximized and provides an initial position, we should launch the maximized window on the monitor where the position is located. + ## UI/UX Design Upon successful implementation, the user is able to add new properties to the json profile file, which is illustrated in the picture below: @@ -86,7 +92,7 @@ For now, this feature only allows the user to set initial positon and choose whe 2. We may need to consider multiple Terminal windows scenario. If the user opens multiple Terminal windows, then we need to consider how to save and restore the position. -3. We may also consider more launch modes. Like full screen mode and minimize mode. +3. We may also consider more launch modes. Like full screen mode and minimized mode. Github issue for future follow-ups: https://github.com/microsoft/terminal/issues/766 diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index f9bc8b766b5..a75ff735b79 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -339,12 +339,15 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - + // - Get the launch mode in json settings file. Now there + // two launch mode: default, maximize. Default means the window + // will launch according to the launch dimensions provided. Maximize + // means the window will launch as a maximized window // Arguments: - // - + // - // Return Value: - // - - std::wstring App::GetLaunchMode() + // - a string representing launch mode: "default" or "maximize" + hstring App::GetLaunchMode() { if (!_loadedInitialSettings) { @@ -356,9 +359,13 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - + // - Get the user defined initial position from Json settings file. + // This position represents the top left corner of the Terminal window. + // This setting is optional, if not provided, we will use the system + // default size, which is provided in IslandWindow::MakeWindow. // Arguments: - // - + // - defaultInitialX: the system default x coordinate value + // - defaultInitialY: the system defualt y coordinate value // Return Value: // - a point containing the requested initial position in pixels. winrt::Windows::Foundation::Point App::GetLaunchInitialPositions(const uint64_t defaultInitialX, const uint64_t defaultInitialY) @@ -369,11 +376,17 @@ namespace winrt::TerminalApp::implementation LoadSettings(); } - winrt::Windows::Foundation::Point point; - point.X = gsl::narrow_cast(_settings->GlobalSettings().GetUseDefaultInitialX() ? defaultInitialX : - _settings->GlobalSettings().GetInitialX()); - point.Y = gsl::narrow_cast(_settings->GlobalSettings().GetUseDefaultInitialY() ? defaultInitialY : - _settings->GlobalSettings().GetInitialY()); + winrt::Windows::Foundation::Point point(defaultInitialX, defaultInitialY); + + if (!_settings->GlobalSettings().GetUseDefaultInitialX()) + { + point.X = _settings->GlobalSettings().GetInitialX(); + } + if (!_settings->GlobalSettings().GetUseDefaultInitialY()) + { + point.Y = _settings->GlobalSettings().GetInitialY(); + } + return point; } diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 129d7a56b34..edd71bdbd07 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -32,7 +32,7 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi); winrt::Windows::Foundation::Point GetLaunchInitialPositions(const uint64_t defaultInitialX, const uint64_t defaultInitialY); - std::wstring GetLaunchMode(); + hstring GetLaunchMode(); bool GetShowTabsInTitlebar(); Windows::UI::Xaml::UIElement GetRoot() noexcept; diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 949dc50bc96..c90ffad764b 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -55,7 +55,7 @@ GlobalAppSettings::GlobalAppSettings() : _requestedTheme{ ElementTheme::Default }, _wordDelimiters{ DEFAULT_WORD_DELIMITERS }, _copyOnSelect{ false }, - _launchMode{ L"default" } + _launchMode{ DEFAULT_LAUNCH_MODE } { } @@ -143,7 +143,7 @@ void GlobalAppSettings::SetCopyOnSelect(const bool copyOnSelect) noexcept _copyOnSelect = copyOnSelect; } -std::wstring GlobalAppSettings::GetLaunchMode() const noexcept +winrt::hstring GlobalAppSettings::GetLaunchMode() const noexcept { return _launchMode; } diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.h b/src/cascadia/TerminalApp/GlobalAppSettings.h index a369e2eb5ec..fc8a10bef6c 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.h +++ b/src/cascadia/TerminalApp/GlobalAppSettings.h @@ -61,7 +61,7 @@ class TerminalApp::GlobalAppSettings final void SetInitialY(const int32_t initialY) noexcept; bool GetUseDefaultInitialY() const noexcept; - std::wstring GetLaunchMode() const noexcept; + winrt::hstring GetLaunchMode() const noexcept; void SetLaunchMode(const std::wstring launchMode); winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme() const noexcept; @@ -80,7 +80,7 @@ class TerminalApp::GlobalAppSettings final int32_t _initialRows; int32_t _initialCols; - int32_t _initialX; + int _initialX; int32_t _initialY; bool _useDefaultInitialX; bool _useDefaultInitialY; @@ -94,7 +94,7 @@ class TerminalApp::GlobalAppSettings final bool _copyOnSelect; winrt::Windows::UI::Xaml::ElementTheme _requestedTheme; - std::wstring _launchMode; + winrt::hstring _launchMode; static winrt::Windows::UI::Xaml::ElementTheme _ParseTheme(const std::wstring& themeString) noexcept; static std::wstring_view _SerializeTheme(const winrt::Windows::UI::Xaml::ElementTheme theme) noexcept; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 36b2c98c875..40600c73b35 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -127,86 +127,102 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& send // to appear on. // Return Value: // - -void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) +winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) { + winrt::hstring launchMode = _app.GetLaunchMode(); + // Acquire the actual intial position winrt::Windows::Foundation::Point initialPosition = _app.GetLaunchInitialPositions(proposedRect.left, proposedRect.top); proposedRect.left = (long)initialPosition.X; - proposedRect.right = (long)initialPosition.Y; - - std::wstring launchMode = _app.GetLaunchMode(); - - // Find nearest montitor. - HMONITOR hmon = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST); + proposedRect.top = (long)initialPosition.Y; - if (launchMode == L"maximize") + long adjustedHeight = 0; + long adjustedWidth = 0; + if (launchMode == L"default") { - MONITORINFO monitorInfo; - monitorInfo.cbSize = sizeof(MONITORINFO); - if (GetMonitorInfoA(hmon, &monitorInfo)) + // Find nearest montitor. + HMONITOR hmon = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST); + + // This API guarantees that dpix and dpiy will be equal, but neither is an + // optional parameter so give two UINTs. + UINT dpix = USER_DEFAULT_SCREEN_DPI; + UINT dpiy = USER_DEFAULT_SCREEN_DPI; + // If this fails, we'll use the default of 96. + GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpix, &dpiy); + + auto initialSize = _app.GetLaunchDimensions(dpix); + + const short _currentWidth = Utils::ClampToShortMax( + static_cast(ceil(initialSize.X)), 1); + const short _currentHeight = Utils::ClampToShortMax( + static_cast(ceil(initialSize.Y)), 1); + + // Create a RECT from our requested client size + auto nonClient = Viewport::FromDimensions({ _currentWidth, + _currentHeight }) + .ToRect(); + + // Get the size of a window we'd need to host that client rect. This will + // add the titlebar space. + if (_useNonClientArea) { - + // If we're in NC tabs mode, do the math ourselves. Get the margins + // we're using for the window - this will include the size of the + // titlebar content. + auto pNcWindow = static_cast(_window.get()); + const MARGINS margins = pNcWindow->GetFrameMargins(); + nonClient.left = 0; + nonClient.top = 0; + nonClient.right = margins.cxLeftWidth + nonClient.right + margins.cxRightWidth; + nonClient.bottom = margins.cyTopHeight + nonClient.bottom + margins.cyBottomHeight; } else { - + bool succeeded = AdjustWindowRectExForDpi(&nonClient, WS_OVERLAPPEDWINDOW, false, 0, dpix); + if (!succeeded) + { + // If we failed to get the correct window size for whatever reason, log + // the error and go on. We'll use whatever the control proposed as the + // size of our window, which will be at least close. + LOG_LAST_ERROR(); + nonClient = Viewport::FromDimensions({ _currentWidth, + _currentHeight }) + .ToRect(); + } } - } - - // This API guarantees that dpix and dpiy will be equal, but neither is an - // optional parameter so give two UINTs. - UINT dpix = USER_DEFAULT_SCREEN_DPI; - UINT dpiy = USER_DEFAULT_SCREEN_DPI; - // If this fails, we'll use the default of 96. - GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpix, &dpiy); - - auto initialSize = _app.GetLaunchDimensions(dpix); - - const short _currentWidth = Utils::ClampToShortMax( - static_cast(ceil(initialSize.X)), 1); - const short _currentHeight = Utils::ClampToShortMax( - static_cast(ceil(initialSize.Y)), 1); - - // Create a RECT from our requested client size - auto nonClient = Viewport::FromDimensions({ _currentWidth, - _currentHeight }) - .ToRect(); - // Get the size of a window we'd need to host that client rect. This will - // add the titlebar space. - if (_useNonClientArea) - { - // If we're in NC tabs mode, do the math ourselves. Get the margins - // we're using for the window - this will include the size of the - // titlebar content. - auto pNcWindow = static_cast(_window.get()); - const MARGINS margins = pNcWindow->GetFrameMargins(); - nonClient.left = 0; - nonClient.top = 0; - nonClient.right = margins.cxLeftWidth + nonClient.right + margins.cxRightWidth; - nonClient.bottom = margins.cyTopHeight + nonClient.bottom + margins.cyBottomHeight; - } - else - { - bool succeeded = AdjustWindowRectExForDpi(&nonClient, WS_OVERLAPPEDWINDOW, false, 0, dpix); - if (!succeeded) + adjustedHeight = nonClient.bottom - nonClient.top; + adjustedWidth = nonClient.right - nonClient.left; + + // We need to check if the top line of the titlebar of the window is winthin any screen + RECT offScreenTestRect; + offScreenTestRect.left = proposedRect.left; + offScreenTestRect.top = proposedRect.top; + offScreenTestRect.right = offScreenTestRect.left + adjustedWidth; + offScreenTestRect.bottom = offScreenTestRect.top + 1; + + bool isTitlebarIntersectWithMonitors = false; + EnumDisplayMonitors(nullptr, &offScreenTestRect, [](HMONITOR hMon, HDC hdc, LPRECT lpr, LPARAM lParam) -> BOOL { + auto intersectWithMonitor = reinterpret_cast(lParam); + *intersectWithMonitor = true; + // Continue the enumeration + return TRUE; + }, reinterpret_cast (&isTitlebarIntersectWithMonitors)); + + if (!isTitlebarIntersectWithMonitors) { - // If we failed to get the correct window size for whatever reason, log - // the error and go on. We'll use whatever the control proposed as the - // size of our window, which will be at least close. - LOG_LAST_ERROR(); - nonClient = Viewport::FromDimensions({ _currentWidth, - _currentHeight }) - .ToRect(); + // If the title bar is out-of-screen, we set the initial position to + // the top left corner of the nearest monitor + MONITORINFO monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfoA(hmon, &monitorInfo); + proposedRect.left = monitorInfo.rcWork.left; + proposedRect.top = monitorInfo.rcWork.top; } } - const auto adjustedHeight = nonClient.bottom - nonClient.top; - const auto adjustedWidth = nonClient.right - nonClient.left; - - const COORD origin{ gsl::narrow(initialPosition.X), - gsl::narrow(initialPosition.Y) }; - + const COORD origin{ gsl::narrow(proposedRect.left), + gsl::narrow(proposedRect.top) }; const COORD dimensions{ Utils::ClampToShortMax(adjustedWidth, 1), Utils::ClampToShortMax(adjustedHeight, 1) }; @@ -220,9 +236,15 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) newPos.Height(), SWP_NOACTIVATE | SWP_NOZORDER); + // Refresh the dpi of HWND becuase the dpi where the window will launch may be different + // at this time + _window->RefreshCurrentDPI(); + // If we can't resize the window, that's really okay. We can just go on with // the originally proposed window size. LOG_LAST_ERROR_IF(!succeeded); + + return _app.GetLaunchMode(); } // Method Description: diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index df747b6bf06..58ecac806e3 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -24,7 +24,7 @@ class AppHost std::unique_ptr _window; winrt::TerminalApp::App _app; - void _HandleCreateWindow(const HWND hwnd, RECT proposedRect); + winrt::hstring _HandleCreateWindow(const HWND hwnd, RECT proposedRect); void _UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::UIElement& arg); void _UpdateTheme(const winrt::TerminalApp::App&, diff --git a/src/cascadia/WindowsTerminal/BaseWindow.h b/src/cascadia/WindowsTerminal/BaseWindow.h index 1ae388dcd37..2141c1d9480 100644 --- a/src/cascadia/WindowsTerminal/BaseWindow.h +++ b/src/cascadia/WindowsTerminal/BaseWindow.h @@ -215,6 +215,15 @@ class BaseWindow PostMessageW(_window.get(), CM_UPDATE_TITLE, 0, reinterpret_cast(nullptr)); } + // Method Description: + // Reset the current dpi of the window. This method is only called after we change the + // initial launch position. This makes sure the dpi is consistent with the monitor on which + // the window will launch + void RefreshCurrentDPI() + { + _currentDpi = GetDpiForWindow(this->_window.get()); + } + protected: using base_type = BaseWindow; wil::unique_hwnd _window; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 881054bb645..b2c5f151845 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -88,7 +88,7 @@ void IslandWindow::Close() // window. // Return Value: // - -void IslandWindow::SetCreateCallback(std::function pfn) noexcept +void IslandWindow::SetCreateCallback(std::function pfn) noexcept { _pfnCreateCallback = pfn; } @@ -110,12 +110,19 @@ void IslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexce rc.right = rc.left + pcs->cx; rc.bottom = rc.top + pcs->cy; + winrt::hstring launchMode = L""; if (_pfnCreateCallback) { - _pfnCreateCallback(_window.get(), rc); + launchMode = _pfnCreateCallback(_window.get(), rc); } - ShowWindow(_window.get(), SW_SHOW); + int nCmdShow = SW_SHOW; + if (launchMode == L"maximized") + { + nCmdShow = SW_MAXIMIZE; + } + + ShowWindow(_window.get(), nCmdShow); UpdateWindow(_window.get()); } diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 1b95b3308f4..1a9bfe1b6d6 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -31,7 +31,7 @@ class IslandWindow : virtual void Initialize(); - void SetCreateCallback(std::function pfn) noexcept; + void SetCreateCallback(std::function pfn) noexcept; void UpdateTheme(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme); @@ -84,7 +84,7 @@ class IslandWindow : winrt::Windows::UI::Xaml::Controls::Grid _rootGrid; - std::function _pfnCreateCallback; + std::function _pfnCreateCallback; void _HandleCreateWindow(const WPARAM wParam, const LPARAM lParam) noexcept; }; diff --git a/src/inc/DefaultSettings.h b/src/inc/DefaultSettings.h index 678f4115b31..667df5c00bd 100644 --- a/src/inc/DefaultSettings.h +++ b/src/inc/DefaultSettings.h @@ -39,3 +39,4 @@ constexpr COLORREF DEFAULT_CURSOR_COLOR = COLOR_WHITE; constexpr COLORREF DEFAULT_CURSOR_HEIGHT = 25; const std::wstring DEFAULT_WORD_DELIMITERS{ L" ./\\()\"'-:,.;<>~!@#$%^&*|+=[]{}~?\u2502" }; +const std::wstring DEFAULT_LAUNCH_MODE{ L"default" }; From 6206ecb547c3d5a7c11af6ed4855a60a8421fe59 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Wed, 18 Sep 2019 18:00:02 -0700 Subject: [PATCH 09/25] remove empty lines --- src/cascadia/TerminalApp/GlobalAppSettings.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index c90ffad764b..8bbec2f0c22 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -21,18 +21,14 @@ static constexpr std::string_view DefaultProfileKey{ "defaultProfile" }; static constexpr std::string_view AlwaysShowTabsKey{ "alwaysShowTabs" }; static constexpr std::string_view InitialRowsKey{ "initialRows" }; static constexpr std::string_view InitialColsKey{ "initialCols" }; - static constexpr std::string_view InitialXKey{ "initialX" }; static constexpr std::string_view InitialYKey{ "initialY" }; - static constexpr std::string_view ShowTitleInTitlebarKey{ "showTerminalTitleInTitlebar" }; static constexpr std::string_view RequestedThemeKey{ "requestedTheme" }; static constexpr std::string_view ShowTabsInTitlebarKey{ "showTabsInTitlebar" }; static constexpr std::string_view WordDelimitersKey{ "wordDelimiters" }; static constexpr std::string_view CopyOnSelectKey{ "copyOnSelect" }; - static constexpr std::string_view LaunchModeKey{ "launchMode" }; - static constexpr std::wstring_view LightThemeValue{ L"light" }; static constexpr std::wstring_view DarkThemeValue{ L"dark" }; static constexpr std::wstring_view SystemThemeValue{ L"system" }; @@ -44,12 +40,10 @@ GlobalAppSettings::GlobalAppSettings() : _alwaysShowTabs{ true }, _initialRows{ DEFAULT_ROWS }, _initialCols{ DEFAULT_COLS }, - _initialX{ 0 }, _initialY{ 0 }, _useDefaultInitialX{ true }, _useDefaultInitialY{ true }, - _showTitleInTitlebar{ true }, _showTabsInTitlebar{ true }, _requestedTheme{ ElementTheme::Default }, From 524f589769209c70c27584b1481e720f1398502f Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Wed, 18 Sep 2019 18:11:57 -0700 Subject: [PATCH 10/25] remove empty lines --- src/cascadia/TerminalApp/GlobalAppSettings.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 8bbec2f0c22..0ef67b1b644 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -213,10 +213,8 @@ Json::Value GlobalAppSettings::ToJson() const jsonObject[JsonKey(DefaultProfileKey)] = winrt::to_string(Utils::GuidToString(_defaultProfile)); jsonObject[JsonKey(InitialRowsKey)] = _initialRows; jsonObject[JsonKey(InitialColsKey)] = _initialCols; - jsonObject[JsonKey(InitialXKey)] = _initialX; jsonObject[JsonKey(InitialYKey)] = _initialY; - jsonObject[JsonKey(AlwaysShowTabsKey)] = _alwaysShowTabs; jsonObject[JsonKey(ShowTitleInTitlebarKey)] = _showTitleInTitlebar; jsonObject[JsonKey(ShowTabsInTitlebarKey)] = _showTabsInTitlebar; @@ -257,7 +255,6 @@ GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json) { result._initialCols = initialCols.asInt(); } - if (auto initialX{ json[JsonKey(InitialXKey)] }) { result._useDefaultInitialX = false; @@ -268,7 +265,6 @@ GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json) result._useDefaultInitialY = false; result._initialY = initialY.asInt(); } - if (auto showTitleInTitlebar{ json[JsonKey(ShowTitleInTitlebarKey)] }) { result._showTitleInTitlebar = showTitleInTitlebar.asBool(); From bea181f2ede14124a8e80d305573100864fec792 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Mon, 23 Sep 2019 11:37:41 -0700 Subject: [PATCH 11/25] Support resize when the Terminal Window hangs off the screen --- .../spec.md | 5 +- src/cascadia/WindowsTerminal/AppHost.cpp | 61 ++++++++++--------- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/doc/specs/#1043 - Set the initial position of the Terminal/spec.md b/doc/specs/#1043 - Set the initial position of the Terminal/spec.md index 2645f6c111f..f9b776aee8d 100644 --- a/doc/specs/#1043 - Set the initial position of the Terminal/spec.md +++ b/doc/specs/#1043 - Set the initial position of the Terminal/spec.md @@ -45,8 +45,9 @@ In step 4, we may need to consider the dpi of the current monitor and multi-moni Edge cases: 1. Multiple monitors. The user should be able to set the initial position to any monitors attached. For the monitors on the left side of the major monitor, the initial position values are negative. -2. If the initial position is larger than the screen resolution and the window is off-screen, we should at least let user be able to see and drag the window back on screen. One solution is to set the initial position to the top left corner of the nearest monitor if the titlebar is off-screen. -3. If the user wants to launch maximized and provides an initial position, we should launch the maximized window on the monitor where the position is located. +2. If the initial position is larger than the screen resolution and the window left corner is off-screen, we should at least let user be able to see and drag the window back on screen. One solution is to set the initial position to the top left corner of the nearest monitor if the top left is off-screen. +3. If the window hangs off the screen, the windows's dimensions should be reduced to ensure that the Terminal fits on screen. +3. If the user wants to launch maximized and provides an initial position, we should launch the maximized window on the top left corner of the monitor where the position is located. ## UI/UX Design diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 40600c73b35..02d9e4fa807 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -143,6 +143,11 @@ winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) // Find nearest montitor. HMONITOR hmon = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST); + // Get nearest monitor information + MONITORINFO monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfoA(hmon, &monitorInfo); + // This API guarantees that dpix and dpiy will be equal, but neither is an // optional parameter so give two UINTs. UINT dpix = USER_DEFAULT_SCREEN_DPI; @@ -150,6 +155,29 @@ winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) // If this fails, we'll use the default of 96. GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpix, &dpiy); + // We need to check if the top left point of the titlebar of the window is winthin any screen + RECT offScreenTestRect; + offScreenTestRect.left = proposedRect.left; + offScreenTestRect.top = proposedRect.top; + offScreenTestRect.right = offScreenTestRect.left + 1; + offScreenTestRect.bottom = offScreenTestRect.top + 1; + + bool isTitlebarIntersectWithMonitors = false; + EnumDisplayMonitors(nullptr, &offScreenTestRect, [](HMONITOR hMon, HDC hdc, LPRECT lpr, LPARAM lParam) -> BOOL { + auto intersectWithMonitor = reinterpret_cast(lParam); + *intersectWithMonitor = true; + // Continue the enumeration + return TRUE; + }, reinterpret_cast(&isTitlebarIntersectWithMonitors)); + + if (!isTitlebarIntersectWithMonitors) + { + // If the title bar is out-of-screen, we set the initial position to + // the top left corner of the nearest monitor + proposedRect.left = monitorInfo.rcWork.left; + proposedRect.top = monitorInfo.rcWork.top; + } + auto initialSize = _app.GetLaunchDimensions(dpix); const short _currentWidth = Utils::ClampToShortMax( @@ -191,34 +219,12 @@ winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) } } - adjustedHeight = nonClient.bottom - nonClient.top; - adjustedWidth = nonClient.right - nonClient.left; + // Calculate the maximum size the window could have without hangs-off + long horizontalMaxLength = std::abs(proposedRect.left - monitorInfo.rcWork.right); + long verticalMaxLength = std::abs(proposedRect.top - monitorInfo.rcWork.bottom); - // We need to check if the top line of the titlebar of the window is winthin any screen - RECT offScreenTestRect; - offScreenTestRect.left = proposedRect.left; - offScreenTestRect.top = proposedRect.top; - offScreenTestRect.right = offScreenTestRect.left + adjustedWidth; - offScreenTestRect.bottom = offScreenTestRect.top + 1; - - bool isTitlebarIntersectWithMonitors = false; - EnumDisplayMonitors(nullptr, &offScreenTestRect, [](HMONITOR hMon, HDC hdc, LPRECT lpr, LPARAM lParam) -> BOOL { - auto intersectWithMonitor = reinterpret_cast(lParam); - *intersectWithMonitor = true; - // Continue the enumeration - return TRUE; - }, reinterpret_cast (&isTitlebarIntersectWithMonitors)); - - if (!isTitlebarIntersectWithMonitors) - { - // If the title bar is out-of-screen, we set the initial position to - // the top left corner of the nearest monitor - MONITORINFO monitorInfo; - monitorInfo.cbSize = sizeof(MONITORINFO); - GetMonitorInfoA(hmon, &monitorInfo); - proposedRect.left = monitorInfo.rcWork.left; - proposedRect.top = monitorInfo.rcWork.top; - } + adjustedHeight = std::min(nonClient.bottom - nonClient.top, verticalMaxLength); + adjustedWidth = std::min(nonClient.right - nonClient.left, horizontalMaxLength); } const COORD origin{ gsl::narrow(proposedRect.left), @@ -227,7 +233,6 @@ winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) Utils::ClampToShortMax(adjustedHeight, 1) }; const auto newPos = Viewport::FromDimensions(origin, dimensions); - bool succeeded = SetWindowPos(hwnd, nullptr, newPos.Left(), From 621c2c9a4567318fe4789a811d6c900d96351372 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Tue, 24 Sep 2019 13:21:02 -0700 Subject: [PATCH 12/25] sync to master, fix conflict and typing warnings --- src/cascadia/TerminalApp/App.cpp | 10 +++++----- src/cascadia/TerminalApp/App.h | 2 +- src/cascadia/TerminalApp/App.idl | 2 +- src/cascadia/TerminalApp/GlobalAppSettings.cpp | 10 +++++----- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 32c46c969cc..197443cdad4 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -366,13 +366,13 @@ namespace winrt::TerminalApp::implementation // - Get the user defined initial position from Json settings file. // This position represents the top left corner of the Terminal window. // This setting is optional, if not provided, we will use the system - // default size, which is provided in IslandWindow::MakeWindow. + // default size, which is provided in IslandWindow::MakeWindow. // Arguments: // - defaultInitialX: the system default x coordinate value // - defaultInitialY: the system defualt y coordinate value // Return Value: // - a point containing the requested initial position in pixels. - winrt::Windows::Foundation::Point App::GetLaunchInitialPositions(const uint64_t defaultInitialX, const uint64_t defaultInitialY) + winrt::Windows::Foundation::Point App::GetLaunchInitialPositions(int defaultInitialX, int defaultInitialY) { if (!_loadedInitialSettings) { @@ -380,15 +380,15 @@ namespace winrt::TerminalApp::implementation LoadSettings(); } - winrt::Windows::Foundation::Point point(defaultInitialX, defaultInitialY); + winrt::Windows::Foundation::Point point((float)defaultInitialX, (float)defaultInitialY); if (!_settings->GlobalSettings().GetUseDefaultInitialX()) { - point.X = _settings->GlobalSettings().GetInitialX(); + point.X = (float)_settings->GlobalSettings().GetInitialX(); } if (!_settings->GlobalSettings().GetUseDefaultInitialY()) { - point.Y = _settings->GlobalSettings().GetInitialY(); + point.Y = (float)_settings->GlobalSettings().GetInitialY(); } return point; diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 8cb970d287d..e2097c46b71 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -31,7 +31,7 @@ namespace winrt::TerminalApp::implementation void LoadSettings(); Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi); - winrt::Windows::Foundation::Point GetLaunchInitialPositions(const uint64_t defaultInitialX, const uint64_t defaultInitialY); + winrt::Windows::Foundation::Point GetLaunchInitialPositions(int defaultInitialX, int defaultInitialY); hstring GetLaunchMode(); bool GetShowTabsInTitlebar(); diff --git a/src/cascadia/TerminalApp/App.idl b/src/cascadia/TerminalApp/App.idl index e41670f8e4c..8b12e7e99be 100644 --- a/src/cascadia/TerminalApp/App.idl +++ b/src/cascadia/TerminalApp/App.idl @@ -24,7 +24,7 @@ namespace TerminalApp String Title { get; }; Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi); - Windows.Foundation.Point GetLaunchInitialPositions(UInt64 defaultInitialX, UInt64 defaultInitialY); + Windows.Foundation.Point GetLaunchInitialPositions(Int32 defaultInitialX, Int32 defaultInitialY); String GetLaunchMode(); Boolean GetShowTabsInTitlebar(); void TitlebarClicked(); diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 4b011153541..fe8d16c3754 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -257,13 +257,13 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) } if (auto initialX{ json[JsonKey(InitialXKey)] }) { - result._useDefaultInitialX = false; - result._initialX = initialX.asInt(); + _useDefaultInitialX = false; + _initialX = initialX.asInt(); } if (auto initialY{ json[JsonKey(InitialYKey)] }) { - result._useDefaultInitialY = false; - result._initialY = initialY.asInt(); + _useDefaultInitialY = false; + _initialY = initialY.asInt(); } if (auto showTitleInTitlebar{ json[JsonKey(ShowTitleInTitlebarKey)] }) { @@ -287,7 +287,7 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) if (auto launchMode{ json[JsonKey(LaunchModeKey)] }) { - result._launchMode = GetWstringFromJson(launchMode); + _launchMode = GetWstringFromJson(launchMode); } if (auto requestedTheme{ json[JsonKey(RequestedThemeKey)] }) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 2aa30d2439c..16a093dd3e4 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -126,7 +126,7 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*se // create. We'll use this rect to determine which monitor the window is about // to appear on. // Return Value: -// - +// - A string that indicates the launch mode winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) { winrt::hstring launchMode = _app.GetLaunchMode(); From e285ac43ea857c53b68e8d67d65b938b3965ed48 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Tue, 24 Sep 2019 14:24:06 -0700 Subject: [PATCH 13/25] Remove unreferenced parameter to fix warnings --- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 16a093dd3e4..33de5ad5bb1 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -163,7 +163,7 @@ winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) offScreenTestRect.bottom = offScreenTestRect.top + 1; bool isTitlebarIntersectWithMonitors = false; - EnumDisplayMonitors(nullptr, &offScreenTestRect, [](HMONITOR hMon, HDC hdc, LPRECT lpr, LPARAM lParam) -> BOOL { + EnumDisplayMonitors(nullptr, &offScreenTestRect, [](HMONITOR, HDC, LPRECT, LPARAM lParam) -> BOOL { auto intersectWithMonitor = reinterpret_cast(lParam); *intersectWithMonitor = true; // Continue the enumeration From 9fc29b6de91c1fe2c2d09dd3cf3d62bec3e868a7 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Tue, 24 Sep 2019 16:08:47 -0700 Subject: [PATCH 14/25] fix format issues --- src/cascadia/WindowsTerminal/AppHost.cpp | 14 ++++++++------ src/cascadia/WindowsTerminal/IslandWindow.cpp | 4 ++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 33de5ad5bb1..98fcb6a171c 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -163,12 +163,14 @@ winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) offScreenTestRect.bottom = offScreenTestRect.top + 1; bool isTitlebarIntersectWithMonitors = false; - EnumDisplayMonitors(nullptr, &offScreenTestRect, [](HMONITOR, HDC, LPRECT, LPARAM lParam) -> BOOL { - auto intersectWithMonitor = reinterpret_cast(lParam); - *intersectWithMonitor = true; - // Continue the enumeration - return TRUE; - }, reinterpret_cast(&isTitlebarIntersectWithMonitors)); + EnumDisplayMonitors( + nullptr, &offScreenTestRect, [](HMONITOR, HDC, LPRECT, LPARAM lParam) -> BOOL { + auto intersectWithMonitor = reinterpret_cast(lParam); + *intersectWithMonitor = true; + // Continue the enumeration + return TRUE; + }, + reinterpret_cast(&isTitlebarIntersectWithMonitors)); if (!isTitlebarIntersectWithMonitors) { diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 481f5d8b6c9..b2c73253d4d 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -121,6 +121,10 @@ void IslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexce { nCmdShow = SW_MAXIMIZE; } + else if (launchMode == L"minimized") + { + nCmdShow = SW_SHOWMINIMIZED; + } ShowWindow(_window.get(), nCmdShow); UpdateWindow(_window.get()); From 9520adb83a2ac515f8e034d38ea1f435b0f880e9 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Tue, 24 Sep 2019 16:11:39 -0700 Subject: [PATCH 15/25] remove support for launch minimization --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index b2c73253d4d..481f5d8b6c9 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -121,10 +121,6 @@ void IslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexce { nCmdShow = SW_MAXIMIZE; } - else if (launchMode == L"minimized") - { - nCmdShow = SW_SHOWMINIMIZED; - } ShowWindow(_window.get(), nCmdShow); UpdateWindow(_window.get()); From 1a24936967621209f6d00075a2e5cb13b1c54d06 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Thu, 26 Sep 2019 19:36:15 -0700 Subject: [PATCH 16/25] Code review changes --- src/cascadia/TerminalApp/App.cpp | 27 +++++++++------ src/cascadia/TerminalApp/App.h | 4 +-- src/cascadia/TerminalApp/App.idl | 10 ++++-- .../TerminalApp/GlobalAppSettings.cpp | 33 ++++++++++++------- src/cascadia/TerminalApp/GlobalAppSettings.h | 10 +++--- src/cascadia/WindowsTerminal/AppHost.cpp | 26 +++++++-------- src/cascadia/WindowsTerminal/AppHost.h | 2 +- src/cascadia/WindowsTerminal/BaseWindow.h | 2 +- src/cascadia/WindowsTerminal/IslandWindow.cpp | 6 ++-- src/cascadia/WindowsTerminal/IslandWindow.h | 5 +-- .../WindowsTerminal/NonClientIslandWindow.h | 1 - 11 files changed, 75 insertions(+), 51 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 197443cdad4..ec558b42146 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -344,14 +344,14 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Get the launch mode in json settings file. Now there - // two launch mode: default, maximize. Default means the window - // will launch according to the launch dimensions provided. Maximize + // two launch mode: default, maximized. Default means the window + // will launch according to the launch dimensions provided. Maximized // means the window will launch as a maximized window // Arguments: // - // Return Value: - // - a string representing launch mode: "default" or "maximize" - hstring App::GetLaunchMode() + // - LaunchMode enum that indicates the launch mode + LaunchMode App::GetLaunchMode() { if (!_loadedInitialSettings) { @@ -359,7 +359,14 @@ namespace winrt::TerminalApp::implementation LoadSettings(); } - return _settings->GlobalSettings().GetLaunchMode(); + hstring launchModeText = _settings->GlobalSettings().GetLaunchMode(); + LaunchMode launchMode = LaunchMode::DefaultMode; + if (launchModeText == L"maximized") + { + launchMode = LaunchMode::MaximizedMode; + } + + return launchMode; } // Method Description: @@ -372,7 +379,7 @@ namespace winrt::TerminalApp::implementation // - defaultInitialY: the system defualt y coordinate value // Return Value: // - a point containing the requested initial position in pixels. - winrt::Windows::Foundation::Point App::GetLaunchInitialPositions(int defaultInitialX, int defaultInitialY) + winrt::Windows::Foundation::Point App::GetLaunchInitialPositions(int32_t defaultInitialX, int32_t defaultInitialY) { if (!_loadedInitialSettings) { @@ -382,13 +389,13 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Foundation::Point point((float)defaultInitialX, (float)defaultInitialY); - if (!_settings->GlobalSettings().GetUseDefaultInitialX()) + if (_settings->GlobalSettings().GetIsInitialXSet()) { - point.X = (float)_settings->GlobalSettings().GetInitialX(); + point.X = gsl::narrow_cast(_settings->GlobalSettings().GetInitialX()); } - if (!_settings->GlobalSettings().GetUseDefaultInitialY()) + if (_settings->GlobalSettings().GetIsInitialYSet()) { - point.Y = (float)_settings->GlobalSettings().GetInitialY(); + point.Y = gsl::narrow_cast(_settings->GlobalSettings().GetInitialY()); } return point; diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index e2097c46b71..3182b797b9d 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -31,8 +31,8 @@ namespace winrt::TerminalApp::implementation void LoadSettings(); Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi); - winrt::Windows::Foundation::Point GetLaunchInitialPositions(int defaultInitialX, int defaultInitialY); - hstring GetLaunchMode(); + winrt::Windows::Foundation::Point GetLaunchInitialPositions(int32_t defaultInitialX, int32_t defaultInitialY); + LaunchMode GetLaunchMode(); bool GetShowTabsInTitlebar(); Windows::UI::Xaml::UIElement GetRoot() noexcept; diff --git a/src/cascadia/TerminalApp/App.idl b/src/cascadia/TerminalApp/App.idl index 8b12e7e99be..72e39f7bb1a 100644 --- a/src/cascadia/TerminalApp/App.idl +++ b/src/cascadia/TerminalApp/App.idl @@ -3,9 +3,16 @@ namespace TerminalApp { + enum LaunchMode + { + DefaultMode, + MaximizedMode, + }; + delegate void LastTabClosedEventArgs(); [default_interface] runtimeclass App : Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication { + App(); void Initialize(); @@ -18,14 +25,13 @@ namespace TerminalApp void Create(); void LoadSettings(); - Windows.UI.Xaml.UIElement GetRoot(); String Title { get; }; Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi); Windows.Foundation.Point GetLaunchInitialPositions(Int32 defaultInitialX, Int32 defaultInitialY); - String GetLaunchMode(); + LaunchMode GetLaunchMode(); Boolean GetShowTabsInTitlebar(); void TitlebarClicked(); diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index fe8d16c3754..18e0334ab9e 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -#include #include "pch.h" #include "GlobalAppSettings.h" #include "../../types/inc/Utils.hpp" @@ -42,8 +41,8 @@ GlobalAppSettings::GlobalAppSettings() : _initialCols{ DEFAULT_COLS }, _initialX{ 0 }, _initialY{ 0 }, - _useDefaultInitialX{ true }, - _useDefaultInitialY{ true }, + _isInitialXSet{ false }, + _isInitialYSet{ false }, _showTitleInTitlebar{ true }, _showTabsInTitlebar{ true }, _requestedTheme{ ElementTheme::Default }, @@ -136,6 +135,7 @@ winrt::hstring GlobalAppSettings::GetLaunchMode() const noexcept { return _launchMode; } + void GlobalAppSettings::SetLaunchMode(const std::wstring launchMode) { _launchMode = launchMode; @@ -156,25 +156,30 @@ int32_t GlobalAppSettings::GetInitialX() const noexcept { return _initialX; } + void GlobalAppSettings::SetInitialX(const int32_t initialX) noexcept { _initialX = initialX; } -bool GlobalAppSettings::GetUseDefaultInitialX() const noexcept + +bool GlobalAppSettings::GetIsInitialXSet() const noexcept { - return _useDefaultInitialX; + return _isInitialXSet; } + int32_t GlobalAppSettings::GetInitialY() const noexcept { return _initialY; } + void GlobalAppSettings::SetInitialY(const int32_t initialY) noexcept { _initialY = initialY; } -bool GlobalAppSettings::GetUseDefaultInitialY() const noexcept + +bool GlobalAppSettings::GetIsInitialYSet() const noexcept { - return _useDefaultInitialY; + return _isInitialYSet; } #pragma endregion @@ -208,8 +213,14 @@ Json::Value GlobalAppSettings::ToJson() const jsonObject[JsonKey(DefaultProfileKey)] = winrt::to_string(Utils::GuidToString(_defaultProfile)); jsonObject[JsonKey(InitialRowsKey)] = _initialRows; jsonObject[JsonKey(InitialColsKey)] = _initialCols; - jsonObject[JsonKey(InitialXKey)] = _initialX; - jsonObject[JsonKey(InitialYKey)] = _initialY; + if (_isInitialXSet) + { + jsonObject[JsonKey(InitialXKey)] = _initialX; + } + if (_isInitialYSet) + { + jsonObject[JsonKey(InitialYKey)] = _initialY; + } jsonObject[JsonKey(AlwaysShowTabsKey)] = _alwaysShowTabs; jsonObject[JsonKey(ShowTitleInTitlebarKey)] = _showTitleInTitlebar; jsonObject[JsonKey(ShowTabsInTitlebarKey)] = _showTabsInTitlebar; @@ -257,12 +268,12 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) } if (auto initialX{ json[JsonKey(InitialXKey)] }) { - _useDefaultInitialX = false; + _isInitialXSet = true; _initialX = initialX.asInt(); } if (auto initialY{ json[JsonKey(InitialYKey)] }) { - _useDefaultInitialY = false; + _isInitialYSet = true; _initialY = initialY.asInt(); } if (auto showTitleInTitlebar{ json[JsonKey(ShowTitleInTitlebarKey)] }) diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.h b/src/cascadia/TerminalApp/GlobalAppSettings.h index 746b49d251a..051965d7c8e 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.h +++ b/src/cascadia/TerminalApp/GlobalAppSettings.h @@ -60,11 +60,11 @@ class TerminalApp::GlobalAppSettings final int32_t GetInitialX() const noexcept; void SetInitialX(const int32_t initialX) noexcept; - bool GetUseDefaultInitialX() const noexcept; + bool GetIsInitialXSet() const noexcept; int32_t GetInitialY() const noexcept; void SetInitialY(const int32_t initialY) noexcept; - bool GetUseDefaultInitialY() const noexcept; + bool GetIsInitialYSet() const noexcept; winrt::hstring GetLaunchMode() const noexcept; void SetLaunchMode(const std::wstring launchMode); @@ -86,10 +86,10 @@ class TerminalApp::GlobalAppSettings final int32_t _initialRows; int32_t _initialCols; - int _initialX; + int32_t _initialX; int32_t _initialY; - bool _useDefaultInitialX; - bool _useDefaultInitialY; + bool _isInitialXSet; + bool _isInitialYSet; bool _showStatusline; bool _alwaysShowTabs; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 98fcb6a171c..3eb3d1bce5f 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -126,19 +126,19 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*se // create. We'll use this rect to determine which monitor the window is about // to appear on. // Return Value: -// - A string that indicates the launch mode -winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) +// - A LaunchMode enum that indicates the launch mode +winrt::TerminalApp::LaunchMode AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) { - winrt::hstring launchMode = _app.GetLaunchMode(); + winrt::TerminalApp::LaunchMode launchMode = _app.GetLaunchMode(); // Acquire the actual intial position winrt::Windows::Foundation::Point initialPosition = _app.GetLaunchInitialPositions(proposedRect.left, proposedRect.top); - proposedRect.left = (long)initialPosition.X; - proposedRect.top = (long)initialPosition.Y; + proposedRect.left = gsl::narrow_cast(initialPosition.X); + proposedRect.top = gsl::narrow_cast(initialPosition.Y); long adjustedHeight = 0; long adjustedWidth = 0; - if (launchMode == L"default") + if (launchMode == winrt::TerminalApp::LaunchMode::DefaultMode) { // Find nearest montitor. HMONITOR hmon = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST); @@ -146,7 +146,7 @@ winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) // Get nearest monitor information MONITORINFO monitorInfo; monitorInfo.cbSize = sizeof(MONITORINFO); - GetMonitorInfoA(hmon, &monitorInfo); + GetMonitorInfo(hmon, &monitorInfo); // This API guarantees that dpix and dpiy will be equal, but neither is an // optional parameter so give two UINTs. @@ -155,7 +155,7 @@ winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) // If this fails, we'll use the default of 96. GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpix, &dpiy); - // We need to check if the top left point of the titlebar of the window is winthin any screen + // We need to check if the top left point of the titlebar of the window is within any screen RECT offScreenTestRect; offScreenTestRect.left = proposedRect.left; offScreenTestRect.top = proposedRect.top; @@ -168,7 +168,7 @@ winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) auto intersectWithMonitor = reinterpret_cast(lParam); *intersectWithMonitor = true; // Continue the enumeration - return TRUE; + return FALSE; }, reinterpret_cast(&isTitlebarIntersectWithMonitors)); @@ -199,7 +199,7 @@ winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) // If we're in NC tabs mode, do the math ourselves. Get the margins // we're using for the window - this will include the size of the // titlebar content. - auto pNcWindow = static_cast(_window.get()); + const auto pNcWindow = static_cast(_window.get()); const MARGINS margins = pNcWindow->GetFrameMargins(); nonClient.left = 0; nonClient.top = 0; @@ -222,8 +222,8 @@ winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) } // Calculate the maximum size the window could have without hangs-off - long horizontalMaxLength = std::abs(proposedRect.left - monitorInfo.rcWork.right); - long verticalMaxLength = std::abs(proposedRect.top - monitorInfo.rcWork.bottom); + const long horizontalMaxLength = std::abs(proposedRect.left - monitorInfo.rcWork.right); + const long verticalMaxLength = std::abs(proposedRect.top - monitorInfo.rcWork.bottom); adjustedHeight = std::min(nonClient.bottom - nonClient.top, verticalMaxLength); adjustedWidth = std::min(nonClient.right - nonClient.left, horizontalMaxLength); @@ -258,7 +258,7 @@ winrt::hstring AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); - return _app.GetLaunchMode(); + return launchMode; } // Method Description: diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 58ecac806e3..d41ad8008d3 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -24,7 +24,7 @@ class AppHost std::unique_ptr _window; winrt::TerminalApp::App _app; - winrt::hstring _HandleCreateWindow(const HWND hwnd, RECT proposedRect); + winrt::TerminalApp::LaunchMode _HandleCreateWindow(const HWND hwnd, RECT proposedRect); void _UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::UIElement& arg); void _UpdateTheme(const winrt::TerminalApp::App&, diff --git a/src/cascadia/WindowsTerminal/BaseWindow.h b/src/cascadia/WindowsTerminal/BaseWindow.h index 9ddfa698c09..25f3d6f41d8 100644 --- a/src/cascadia/WindowsTerminal/BaseWindow.h +++ b/src/cascadia/WindowsTerminal/BaseWindow.h @@ -232,7 +232,7 @@ class BaseWindow // the window will launch void RefreshCurrentDPI() { - _currentDpi = GetDpiForWindow(this->_window.get()); + _currentDpi = GetDpiForWindow(_window.get()); } protected: diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 481f5d8b6c9..9bb6765ec60 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -88,7 +88,7 @@ void IslandWindow::Close() // window. // Return Value: // - -void IslandWindow::SetCreateCallback(std::function pfn) noexcept +void IslandWindow::SetCreateCallback(std::function pfn) noexcept { _pfnCreateCallback = pfn; } @@ -110,14 +110,14 @@ void IslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexce rc.right = rc.left + pcs->cx; rc.bottom = rc.top + pcs->cy; - winrt::hstring launchMode = L""; + winrt::TerminalApp::LaunchMode launchMode = winrt::TerminalApp::LaunchMode::DefaultMode; if (_pfnCreateCallback) { launchMode = _pfnCreateCallback(_window.get(), rc); } int nCmdShow = SW_SHOW; - if (launchMode == L"maximized") + if (launchMode == winrt::TerminalApp::LaunchMode::MaximizedMode) { nCmdShow = SW_MAXIMIZE; } diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 9d2e553c986..84136d706bc 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -8,6 +8,7 @@ #include #include #include "../../cascadia/inc/cppwinrt_utils.h" +#include class IslandWindow : public BaseWindow, @@ -31,7 +32,7 @@ class IslandWindow : virtual void Initialize(); - void SetCreateCallback(std::function pfn) noexcept; + void SetCreateCallback(std::function pfn) noexcept; void UpdateTheme(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme); @@ -84,7 +85,7 @@ class IslandWindow : winrt::Windows::UI::Xaml::Controls::Grid _rootGrid; - std::function _pfnCreateCallback; + std::function _pfnCreateCallback; void _HandleCreateWindow(const WPARAM wParam, const LPARAM lParam) noexcept; }; diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h index 161a182576b..69e8f5dbd76 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h @@ -21,7 +21,6 @@ Author(s): #include "IslandWindow.h" #include "../../types/inc/Viewport.hpp" #include -#include #include class NonClientIslandWindow : public IslandWindow From 9433b727cc5cea3b65b96c59cd9592a52b74b14b Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Fri, 27 Sep 2019 10:54:37 -0700 Subject: [PATCH 17/25] remove spec in this PR --- .../images/ProfileSnapshot.png | Bin 7153 -> 0 bytes .../spec.md | 103 ------------------ 2 files changed, 103 deletions(-) delete mode 100644 doc/specs/#1043 - Set the initial position of the Terminal/images/ProfileSnapshot.png delete mode 100644 doc/specs/#1043 - Set the initial position of the Terminal/spec.md diff --git a/doc/specs/#1043 - Set the initial position of the Terminal/images/ProfileSnapshot.png b/doc/specs/#1043 - Set the initial position of the Terminal/images/ProfileSnapshot.png deleted file mode 100644 index c73633a3855f75e20ed71433abe1e182c08a0ba6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7153 zcmeHKX;@Ng*WTKo=1EI4%SlJqYhU{r_Fm80>sime?mN!T2C{vd z$~FK1wp(2|dj$YCX#jwvv-DQc$di!!cSR2gd)oRm0F>ip`JP)u zeVMx#t|I|J?t}Q2P&#rk3IO)SSe-rX80O9##XU|(WiPPh!K8a>>i1g;qFV5FEhIau zE9h$j7e0NV_ZC}lzgEHOq6hVDXI)q(*34Ue=#vf!ST`rrSl{+L=Z15%)cJ9RAv=^7 zl~WIz`rB)KykKAfHRqzJ%F=UxJTP9Gz7^5Xm1#(wMSCE-x8v9oXl!X=cU8Q|w+lVh zd^rH

LK3fRyth&j7z)&0lf&l?1;I!!IQGZx9g`urkxfkOroXt=%a+(kcR0P}??X zyJ{B~m$scikE`^KJXmFAWtt|CSG_~SVpQdXzoUsH5Fv}U0D!1JuUSOBuS77~5SxMJ z-IAMMyE2)~pVr*&Z~0o>nPmjAw6qKtO>ULkEP`>m!qGffv2*z|RSBRW8jzT|CfpFT zi`S{yEGwFaZV{Q-X9PP6$qqo%dNaaD9oW24dD32^%EuK-f%=p_k6OcxX)Ojk|0KB> zsZ5zO8%S%-{x*zESc2i{dJWit?nZMp3GoiUH95`} z2aCXPm?9Hf$+Z>TiM3xKk6WWJTJUEDY=Lrn%YIYSYE5#up%**$JS78h3;p=(eeZxn zCs}cRX<-qK99q^Fge7q--*)*8#(I6ikl}5BjX;&GomhKT0}b>+2%gn=yO~rEuL3=f zenufZJ^i-7zYcyZub;vGq+d&%5yW~E-PsWMthUHhJdzNW7QQt$zNA$`s@8dAxOelkO?IYY_I(T)tdf zQ1{S=ulB+5=CGNCA8b;>b~uCFN*Y{DlT-3Es4^LQfA>0O7L>#o&rxbNK(?=xhjo!$ zZWFOc;z}+xp8IDB{v3h0gJ4~w*%I>yZ@W*_4C}FPm*R5dA|@>)PL*>c{GglZhB4v2y%n&5upb z+dowra63}_uBHSg=qG72jsnYnN=SY!?(gqMIL=p5p34qUb}B#C)u<~@o-1L-Y0(N! zXwusfv?%NDn5PdwhevPoYBK&rg&aMqUpiE?_|iC-R2~>3|jzi{o z2z{}aK`R-1%pNsb&%7PK zr04$-pHvih66`Y#DI_4agP9KOG2v^X!<{qg@avar5H^liUib@;?R1F8YD8d#DG+R! z)g!U-5C4V2m7w{#uAApRc8n+t=tqJs0(o`L5~A%XxLVk@bJVBz*+XrIgNe%4$v!R1!2WT^enzuUR+o*viGCTpEC3t+UR{X`n^D?`J>?e>%LuB@(l)|)UsONy456%tYAv2wD8!9>=*(}rc& z{c(%>?VDmp^{^N${HrHM8AxWcZeO?Xj(NmV1A5g&WL{GQXDhFa^5j(&b2mnod3SuP ziKCu&M6>md3^VLx{tG8bn#cV7_tW?GXAh^+>i3rOEH$B*O&rk4A7||yvF=uEObRP( zkLRjt3hkTPmyn2*o9)~42XeUfn54$n91MOXf%Z3?F#?<1WMTVON`!Vf|!2l{0Y(Of`s zah~I?GTXQ&1rLzA@hKT6aMn!ESSi(AI3YWfEY&u1#63&!^#GfB-P$Y}jPS5@3uo6j zpX4@&ak6imy|QyRG>cv=OCVRrw`PVul}^rdEz!?KxXUC}1SMB&RD>z4tvLsOl7WBw z%Z!GW+UT5}+%=DD`^*HlmFSjNyG8Exf;p4CBo_!~gt|eDkoZR8I`;kqFjid!$QCzM5 zxLF-GwHpgP19c+UFdxjkyjm$x%Re^aY{U8#W&EQ9oP0pnBca0fO>um%504a=<~;~$ z*GtyaqN@5H3)hD6{+U7`0y{uRZH@>K$>i z2yxv%-4oSp&ip)bqa)bqQTPwdEU%4F7~XFHbagnlHx;o$dly~I zrkfE+@J>9*vQK#{*^jfhI5g54ZPL1S?U#kIjE_g(Tg zFy70VoX)VrPiXZCDm18OKvt*i^|juamu@Y#>yGtYu+HSJtR9HlZp9L!V%n}ZF2a#T zUawQ5`*4vQFxqiA_-8q%by8&#j7p?4GhJzG#eZIgrTYv%|gW445a z{98#$0C*w(O@BYXxL2Mng{Rh!7M>97hgq1Z3B*oSw}-ANFy+m-;G#a zM}GgR`)s~BNEZ)8-3N&c|1S#>gLLx)E-hR|iF_>7*PZKV9?9JVr&u4W{M4CGp^lv} zdo{Y^#7^?)e--gLW^znD*S*iW*>R+iJHxD&WI4y;Ca4cO+e;pPdY%Qf3LkmXzXiUL zf~3DvZi50F8ZshBid`xTRGANAq-X*2Im$@+AYHA+%HlASF6~@_JzCFd^=PX53(vMm zI-Br>zF)*EVsezJ*p>ba)Y|?QOb^7zJ?dF`ncw?fvJZ@-{ROgFo^ux9w z)f4^t(o)+7u)eJ=a-7c7tPK7Vc+Tp5Y4Ix(M=$v_hu1(Q1N ztBksU$uUDi7`F0c=$z{sS%_Wu=DcwkT_5C9`^@1~URl`fA6@cVMdvR1uw-p1_1i0J zXB_&28uLR~HWC%BU}Sk)kM9NIF!{LU?0s2!_B>G)8*>?NqI|89P`Ia9wyP9GEaT}+ zhFoV1`YAzMYt4$I?znTh;hyY9?DShuw8j48-Vvy(0%pxE76nR?*E~a+5wP_aHE_oFjFn^> zXoAwT`)cKHHvGuCt%a@BmECxHY`c6W((}dbOZJ4GXdzJc5;gGH9%Pcz(x4a!f zE>)Wvk7GqYog~-it0x7`k&m}L&3%6;-JLVbBcpYw`m5GO1$;}~R5&$>Hun2)RVDdz zf?A^b)%ZMTMvd@EE;+n2lybkj1CPupD)w^h4s=!VhzwYbxtc{M8wNexUR|#n*~7~m zTwu09&oSnU-(PtfXgRXDvH0#x%VQp&PM$C5=vGnkaJF-`^1HE+nSrypw1Rv$YII_Q zNnwU>vIx=;#jmfY$1eGO_iUjuZf^z7po}>BXuxYEFPA{SvcAA0OBB-oI>2DdH6_G7 z=qJ##Rqx)dFVyz>{xp`7F+&Pqp9!GeR?ya)L$kwU=j2cw@Ja|gXQuqm)6R8bal zt0-?@wg>2Hc);S!++hhGX{9Wy7E+wgF4V1?sz!~xvr=i)fRG}`<-L1aJvP1|svaic z;sPQ$z6L@Nb&94?5EHiE8+)x$KJiW8{EdJc3vAxFz3j8vi2Y6hBLYDl zA@d1j``c92W3&D(RTsmGK;wDF1;3@v%uxE1$_pZ7`lI%MpB=Et*+?GRc7ff8p}|r= zXS>iL1E*ATgS16(>@xKF{D-Mh%GV%FfvbL;&i%=v)kzXQV8tGOGtrDQ%8rhW@3vcy zSn0oRcE6y8G#hl|(Uvj~aow1qHsMSn=>EF<$vUk)Jl3OOuK%65-t8h*{rBiKi?vdG zOSP(^dVN4hJ-2of^uxW%X}J^TI|%A*2s=)W6U7h3dYz%${IEyeB%+&Nhy5_C1ZjMM z4ej#H{p{%Xyc~{bQuaq%G;a9+rd^Fv#VlqR&!iq=sHF_oHPZXsSrXIE40T~p_{pxI zA=H)^gR=&6k09jxr8jmFFaiE<>?$u0%7k+Wd3?%nfmg#X(WhA5;CBs0rh2k{mtcjB z!l{WveIN}XUzyNK8}7ot+P@A`@oFP}V^=(C18LMX%%@MPCF9oSlFT?npiIctIFskJ zsPSR!jkAG?;1ZpVHY<(?%5de9qTNc`XOylP@u3OKo@X4PaeQIaNxIQeMRZA5Lm)Y@ z4_+RB{0at`;}(f2?cgTFaxa7c2})E-=>j$VSas%qrrj3aKqsmlH_dTd@>V@Qm12a! zOr4HpEp@}d8;nxix9M8zmM1hUq_LDg4$RGq7@bpp2^QN&Pjl)gHA~@-;!Qb~rPxEa zI@gx=7*d3B4(clVxGb3s0QXw_a^(8(KwIzFYAlX-1;cAmPk!}^g}Ws^m#|50$|)I` zdiXDyKrd^N)5V+ikBq3QuYQC?j6aPAEVvai`(L{j7Z?AVO{(nM_fwOKt5DLO@^wqT zNJVCg)#{~7%C>^70LM%$bprzf|NY7NB$cblK-Ab7F*8`WDIdHkYK(&XmqZo|<)89b y^XI^?=C3&X%7+WTlHgYo{7QoVTp51fDOA@yvX>ufM=S$Gm(@9&vn6M4-v1x|I!@jI diff --git a/doc/specs/#1043 - Set the initial position of the Terminal/spec.md b/doc/specs/#1043 - Set the initial position of the Terminal/spec.md deleted file mode 100644 index f9b776aee8d..00000000000 --- a/doc/specs/#1043 - Set the initial position of the Terminal/spec.md +++ /dev/null @@ -1,103 +0,0 @@ ---- -author: Kaiyu Wang KaiyuWang16/kawa@microsoft.com -created on: 2019-09-03 -last updated: 2019-09-03 -issue id: #1043 ---- - -# Set the initial position for terminal - -## Abstract - -This spec is for task #1043 “Be able to set an initial position for the terminal”. It goes over the details of a new feature that allows users to set the initial position and size of the terminal. Expected behavior and design of this feature is included. Besides, future possible follow-up works are also addressed. - -## Inspiration - -The idea is to allow users to set the initial position of the Terminal when they launch it, prevent the Terminal from appearing on unexpected position (e.g. outside of the screen bounds). We are also going to let users choose to maximize the window when they launch it. - -## Solution Design - -For now, the Terminal window is put on a default initial position. The program uses CW_USEDEFAULT in the screen coordinates for top-left corner. We have two different types of window – client window and non-client window. However, code path for window creation (WM_CREATE message is shared by the two types of windows) are almost the same for the two types of windows, except that there are some differences in calculation of the width and height of the window. - -Four new properties should be added in the json settings file: - -**initialX**: int. This set the initial horizontal position of the top-left corner of the window. This property is optional. If not provided, system default position will be used. - -**initialY**: int. This set the initial vertical position of the top-left corner of the window. This property is optional. If not provided, system default position will be used. - -**launchMode**: string. Determine the launch mode. There are two modes for now - 1. maximize: the window will be maximized when launch. - 2. default: the window will be initialized with system default size. - -The steps of this process: - -1. Set the top-left origin, width and height to CW_USEDEFAULT. -2. Get the dpi of the nearest monitor; Load settings. -3. From settings, find the user-defined initial position and launch mode. -4. If the user sets custom initial position, calculate the new position considering the current dpi and monitor. If not, use system default value. -5. If the user set launch mode as "maximize", calculate the new height and width. If the user choose "default", use system default size. -6. SetWindowPos with the new position and dimension of the window. - -Step 2 to 6 should be done in `AppHost::_HandleCreateWindow`, which is consistent to the current code. - -In step 4, we may need to consider the dpi of the current monitor and multi-monitor scenario when calculating the initial position of the window. - -Edge cases: - -1. Multiple monitors. The user should be able to set the initial position to any monitors attached. For the monitors on the left side of the major monitor, the initial position values are negative. -2. If the initial position is larger than the screen resolution and the window left corner is off-screen, we should at least let user be able to see and drag the window back on screen. One solution is to set the initial position to the top left corner of the nearest monitor if the top left is off-screen. -3. If the window hangs off the screen, the windows's dimensions should be reduced to ensure that the Terminal fits on screen. -3. If the user wants to launch maximized and provides an initial position, we should launch the maximized window on the top left corner of the monitor where the position is located. - -## UI/UX Design - -Upon successful implementation, the user is able to add new properties to the json profile file, which is illustrated in the picture below: - -![Sol Design](images/ProfileSnapshot.png) - -The rest of the UI will be the same of the current Terminal experience, except that the initial position may be different. - -## Capabilities - -### Accessibility - -This feature will not impact accessibility of Windows Terminal. - -### Security - -This should not introduce any new security issues. - -### Reliability - -This new feature allows the users to set custom initial position of the Terminal App window, which helps them to avoid embarassing window launch situation such as launching outside the screen bounds. Thus, it improves the reliability. - -### Compatibility - -This feature won't break existing features of Terminal. - -### Performance, Power, and Efficiency - -More data reading and calculation will be included in Terminal Launch process, which may inversely influence the launch time. However, the impact is trivial. - -## Potential Issues - -1. The dpi of the monitor may impact the initial position even if the json profile settings are the same. We need to consider the dpi of the user's monitor and calculate the initial position in the current screen coordinates. Testing with different monitor dpi are also necessary. - -2. We need to consider multi-monitor scenario. If the user has multiple monitors, we must guarantee that the Terminal could be iniitalized as expected. More discussions on what should we do in this scenario with the github community is needed. - -## Future considerations - -For now, this feature only allows the user to set initial positon and choose whether to maximize the window when launch. In the future, we may consider follow-up features like: - -1. Save the position of the Terminal on exit, and restore the position on the next launch. This could be a true/false feature that users could choose to set. - -2. We may need to consider multiple Terminal windows scenario. If the user opens multiple Terminal windows, then we need to consider how to save and restore the position. - -3. We may also consider more launch modes. Like full screen mode and minimized mode. - -Github issue for future follow-ups: https://github.com/microsoft/terminal/issues/766 - -## Resources - -Github issue: -https://github.com/microsoft/terminal/issues/1043 From a682e6df456cb6258e29817183cc9de8c96670e0 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Fri, 27 Sep 2019 21:47:04 -0700 Subject: [PATCH 18/25] Move launchMode0 string serializarion to GlobalAppSettings --- src/cascadia/TerminalApp/App.cpp | 9 +--- .../TerminalApp/GlobalAppSettings.cpp | 47 +++++++++++++++++-- src/cascadia/TerminalApp/GlobalAppSettings.h | 9 ++-- src/cascadia/WindowsTerminal/AppHost.cpp | 15 +++--- src/cascadia/WindowsTerminal/IslandWindow.cpp | 9 ++++ src/cascadia/WindowsTerminal/IslandWindow.h | 1 + 6 files changed, 68 insertions(+), 22 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index ec558b42146..cfbebb400aa 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -359,14 +359,7 @@ namespace winrt::TerminalApp::implementation LoadSettings(); } - hstring launchModeText = _settings->GlobalSettings().GetLaunchMode(); - LaunchMode launchMode = LaunchMode::DefaultMode; - if (launchModeText == L"maximized") - { - launchMode = LaunchMode::MaximizedMode; - } - - return launchMode; + return _settings->GlobalSettings().GetLaunchMode(); } // Method Description: diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 18e0334ab9e..86bc4999b94 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -28,6 +28,8 @@ static constexpr std::string_view ShowTabsInTitlebarKey{ "showTabsInTitlebar" }; static constexpr std::string_view WordDelimitersKey{ "wordDelimiters" }; static constexpr std::string_view CopyOnSelectKey{ "copyOnSelect" }; static constexpr std::string_view LaunchModeKey{ "launchMode" }; +static constexpr std::wstring_view DefaultLaunchModeValue{ L"default" }; +static constexpr std::wstring_view MaximizedLaunchModeValue{ L"maximized" }; static constexpr std::wstring_view LightThemeValue{ L"light" }; static constexpr std::wstring_view DarkThemeValue{ L"dark" }; static constexpr std::wstring_view SystemThemeValue{ L"system" }; @@ -48,7 +50,7 @@ GlobalAppSettings::GlobalAppSettings() : _requestedTheme{ ElementTheme::Default }, _wordDelimiters{ DEFAULT_WORD_DELIMITERS }, _copyOnSelect{ false }, - _launchMode{ DEFAULT_LAUNCH_MODE } + _launchMode{ LaunchMode::DefaultMode } { } @@ -131,12 +133,12 @@ void GlobalAppSettings::SetCopyOnSelect(const bool copyOnSelect) noexcept _copyOnSelect = copyOnSelect; } -winrt::hstring GlobalAppSettings::GetLaunchMode() const noexcept +LaunchMode GlobalAppSettings::GetLaunchMode() const noexcept { return _launchMode; } -void GlobalAppSettings::SetLaunchMode(const std::wstring launchMode) +void GlobalAppSettings::SetLaunchMode(const LaunchMode launchMode) { _launchMode = launchMode; } @@ -226,7 +228,7 @@ Json::Value GlobalAppSettings::ToJson() const jsonObject[JsonKey(ShowTabsInTitlebarKey)] = _showTabsInTitlebar; jsonObject[JsonKey(WordDelimitersKey)] = winrt::to_string(_wordDelimiters); jsonObject[JsonKey(CopyOnSelectKey)] = _copyOnSelect; - jsonObject[JsonKey(LaunchModeKey)] = winrt::to_string(_launchMode); + jsonObject[JsonKey(LaunchModeKey)] = winrt::to_string(_SerializeLaunchMode(_launchMode)); jsonObject[JsonKey(RequestedThemeKey)] = winrt::to_string(_SerializeTheme(_requestedTheme)); jsonObject[JsonKey(KeybindingsKey)] = _keybindings->ToJson(); @@ -298,7 +300,7 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) if (auto launchMode{ json[JsonKey(LaunchModeKey)] }) { - _launchMode = GetWstringFromJson(launchMode); + _launchMode = _ParseLaunchMode(GetWstringFromJson(launchMode)); } if (auto requestedTheme{ json[JsonKey(RequestedThemeKey)] }) @@ -352,3 +354,38 @@ std::wstring_view GlobalAppSettings::_SerializeTheme(const ElementTheme theme) n return SystemThemeValue; } } + +// Method Description: +// - Helper function for converting the user-specified launch mode +// to a LaunchMode enum value +// Arguments: +// - launchModeString: The string value from the settings file to parse +// Return Value: +// - The corresponding enum value which maps to the string provided by the user +LaunchMode GlobalAppSettings::_ParseLaunchMode(const std::wstring& launchModeString) noexcept +{ + if (launchModeString == MaximizedLaunchModeValue) + { + return LaunchMode::MaximizedMode; + } + + return LaunchMode::DefaultMode; +} + +// Method Description: +// - Helper function for converting a LaunchMode to its corresponding string +// value. +// Arguments: +// - launchMode: The enum value to convert to a string. +// Return Value: +// - The string value for the given CursorStyle +std::wstring_view GlobalAppSettings::_SerializeLaunchMode(const LaunchMode launchMode) noexcept +{ + switch (launchMode) + { + case LaunchMode::MaximizedMode: + return MaximizedLaunchModeValue; + default: + return DefaultLaunchModeValue; + } +} diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.h b/src/cascadia/TerminalApp/GlobalAppSettings.h index 051965d7c8e..0b677706bf0 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.h +++ b/src/cascadia/TerminalApp/GlobalAppSettings.h @@ -66,8 +66,8 @@ class TerminalApp::GlobalAppSettings final void SetInitialY(const int32_t initialY) noexcept; bool GetIsInitialYSet() const noexcept; - winrt::hstring GetLaunchMode() const noexcept; - void SetLaunchMode(const std::wstring launchMode); + winrt::TerminalApp::LaunchMode GetLaunchMode() const noexcept; + void SetLaunchMode(const winrt::TerminalApp::LaunchMode launchMode); winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme() const noexcept; @@ -100,10 +100,13 @@ class TerminalApp::GlobalAppSettings final bool _copyOnSelect; winrt::Windows::UI::Xaml::ElementTheme _requestedTheme; - winrt::hstring _launchMode; + winrt::TerminalApp::LaunchMode _launchMode; static winrt::Windows::UI::Xaml::ElementTheme _ParseTheme(const std::wstring& themeString) noexcept; static std::wstring_view _SerializeTheme(const winrt::Windows::UI::Xaml::ElementTheme theme) noexcept; + static std::wstring_view _SerializeLaunchMode(const winrt::TerminalApp::LaunchMode launchMode) noexcept; + static winrt::TerminalApp::LaunchMode _ParseLaunchMode(const std::wstring& launchModeString) noexcept; + friend class TerminalAppLocalTests::SettingsTests; }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 3eb3d1bce5f..0bc8003120b 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -219,14 +219,17 @@ winrt::TerminalApp::LaunchMode AppHost::_HandleCreateWindow(const HWND hwnd, REC _currentHeight }) .ToRect(); } - } - // Calculate the maximum size the window could have without hangs-off - const long horizontalMaxLength = std::abs(proposedRect.left - monitorInfo.rcWork.right); - const long verticalMaxLength = std::abs(proposedRect.top - monitorInfo.rcWork.bottom); + // For client island scenario, there is an invisible border of 8 pixals. + // We need to remove this border to guarantee the left edge of the window + // coincides with the screen + const auto pCWindow = static_cast(_window.get()); + const RECT frame = pCWindow->GetFrameBorderMargins(dpix); + proposedRect.left -= (frame.left * -1); + } - adjustedHeight = std::min(nonClient.bottom - nonClient.top, verticalMaxLength); - adjustedWidth = std::min(nonClient.right - nonClient.left, horizontalMaxLength); + adjustedHeight = nonClient.bottom - nonClient.top; + adjustedWidth = nonClient.right - nonClient.left; } const COORD origin{ gsl::narrow(proposedRect.left), diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 9bb6765ec60..f7a2c0f9a39 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -235,6 +235,15 @@ IRawElementProviderSimple* IslandWindow::_GetUiaProvider() return _pUiaProvider; } +RECT IslandWindow::GetFrameBorderMargins(unsigned int currentDpi) +{ + const auto windowStyle = GetWindowStyle(_window.get()); + const auto targetStyle = windowStyle & ~WS_DLGFRAME; + RECT frame{}; + AdjustWindowRectExForDpi(&frame, targetStyle, false, GetWindowExStyle(_window.get()), currentDpi); + return frame; +} + // Method Description: // - Called when the window has been resized (or maximized) // Arguments: diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 84136d706bc..316bce74b51 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -24,6 +24,7 @@ class IslandWindow : [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override; IRawElementProviderSimple* _GetUiaProvider(); + RECT GetFrameBorderMargins(unsigned int currentDpi); void OnResize(const UINT width, const UINT height) override; void OnMinimize() override; void OnRestore() override; From 1a330d7f696c115ef75c87885e48a65fc89529ea Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Mon, 30 Sep 2019 11:25:24 -0700 Subject: [PATCH 19/25] remove useless default value definition and fix typos --- src/cascadia/TerminalApp/GlobalAppSettings.cpp | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- src/inc/DefaultSettings.h | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 86bc4999b94..1620a719486 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -378,7 +378,7 @@ LaunchMode GlobalAppSettings::_ParseLaunchMode(const std::wstring& launchModeStr // Arguments: // - launchMode: The enum value to convert to a string. // Return Value: -// - The string value for the given CursorStyle +// - The string value for the given LaunchMode std::wstring_view GlobalAppSettings::_SerializeLaunchMode(const LaunchMode launchMode) noexcept { switch (launchMode) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 0bc8003120b..d282897b98d 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -220,7 +220,7 @@ winrt::TerminalApp::LaunchMode AppHost::_HandleCreateWindow(const HWND hwnd, REC .ToRect(); } - // For client island scenario, there is an invisible border of 8 pixals. + // For client island scenario, there is an invisible border of 8 pixels. // We need to remove this border to guarantee the left edge of the window // coincides with the screen const auto pCWindow = static_cast(_window.get()); diff --git a/src/inc/DefaultSettings.h b/src/inc/DefaultSettings.h index 37e80520ded..ed6d8f2a2f7 100644 --- a/src/inc/DefaultSettings.h +++ b/src/inc/DefaultSettings.h @@ -43,6 +43,4 @@ constexpr COLORREF DEFAULT_CURSOR_COLOR = COLOR_WHITE; constexpr COLORREF DEFAULT_CURSOR_HEIGHT = 25; const std::wstring DEFAULT_WORD_DELIMITERS{ L" ./\\()\"'-:,.;<>~!@#$%^&*|+=[]{}~?\u2502" }; - -const std::wstring DEFAULT_LAUNCH_MODE{ L"default" }; #pragma warning(pop) From a7cc38867880dbdadd1896fa5bee1732eb57aa5f Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Wed, 9 Oct 2019 17:59:49 -0700 Subject: [PATCH 20/25] Use std::optional<> for initial X/Y value, fix format issue --- src/cascadia/TerminalApp/App.cpp | 8 ++++---- .../TerminalApp/GlobalAppSettings.cpp | 20 +++++++++---------- src/cascadia/TerminalApp/GlobalAppSettings.h | 12 +++++------ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index cfbebb400aa..85c201632d2 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -382,13 +382,13 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Foundation::Point point((float)defaultInitialX, (float)defaultInitialY); - if (_settings->GlobalSettings().GetIsInitialXSet()) + if (_settings->GlobalSettings().IsInitialXSet()) { - point.X = gsl::narrow_cast(_settings->GlobalSettings().GetInitialX()); + point.X = gsl::narrow_cast(_settings->GlobalSettings().GetInitialX().value()); } - if (_settings->GlobalSettings().GetIsInitialYSet()) + if (_settings->GlobalSettings().IsInitialYSet()) { - point.Y = gsl::narrow_cast(_settings->GlobalSettings().GetInitialY()); + point.Y = gsl::narrow_cast(_settings->GlobalSettings().GetInitialY().value()); } return point; diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 1620a719486..8e10e0bb929 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -154,7 +154,7 @@ void GlobalAppSettings::SetShowTabsInTitlebar(const bool showTabsInTitlebar) noe _showTabsInTitlebar = showTabsInTitlebar; } -int32_t GlobalAppSettings::GetInitialX() const noexcept +std::optional GlobalAppSettings::GetInitialX() const noexcept { return _initialX; } @@ -164,12 +164,12 @@ void GlobalAppSettings::SetInitialX(const int32_t initialX) noexcept _initialX = initialX; } -bool GlobalAppSettings::GetIsInitialXSet() const noexcept +bool GlobalAppSettings::IsInitialXSet() const noexcept { return _isInitialXSet; } -int32_t GlobalAppSettings::GetInitialY() const noexcept +std::optional GlobalAppSettings::GetInitialY() const noexcept { return _initialY; } @@ -179,7 +179,7 @@ void GlobalAppSettings::SetInitialY(const int32_t initialY) noexcept _initialY = initialY; } -bool GlobalAppSettings::GetIsInitialYSet() const noexcept +bool GlobalAppSettings::IsInitialYSet() const noexcept { return _isInitialYSet; } @@ -217,11 +217,11 @@ Json::Value GlobalAppSettings::ToJson() const jsonObject[JsonKey(InitialColsKey)] = _initialCols; if (_isInitialXSet) { - jsonObject[JsonKey(InitialXKey)] = _initialX; + jsonObject[JsonKey(InitialXKey)] = _initialX.value(); } if (_isInitialYSet) { - jsonObject[JsonKey(InitialYKey)] = _initialY; + jsonObject[JsonKey(InitialYKey)] = _initialY.value(); } jsonObject[JsonKey(AlwaysShowTabsKey)] = _alwaysShowTabs; jsonObject[JsonKey(ShowTitleInTitlebarKey)] = _showTitleInTitlebar; @@ -383,9 +383,9 @@ std::wstring_view GlobalAppSettings::_SerializeLaunchMode(const LaunchMode launc { switch (launchMode) { - case LaunchMode::MaximizedMode: - return MaximizedLaunchModeValue; - default: - return DefaultLaunchModeValue; + case LaunchMode::MaximizedMode: + return MaximizedLaunchModeValue; + default: + return DefaultLaunchModeValue; } } diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.h b/src/cascadia/TerminalApp/GlobalAppSettings.h index 0b677706bf0..be16a96932b 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.h +++ b/src/cascadia/TerminalApp/GlobalAppSettings.h @@ -58,13 +58,13 @@ class TerminalApp::GlobalAppSettings final bool GetCopyOnSelect() const noexcept; void SetCopyOnSelect(const bool copyOnSelect) noexcept; - int32_t GetInitialX() const noexcept; + std::optional GetInitialX() const noexcept; void SetInitialX(const int32_t initialX) noexcept; - bool GetIsInitialXSet() const noexcept; + bool IsInitialXSet() const noexcept; - int32_t GetInitialY() const noexcept; + std::optional GetInitialY() const noexcept; void SetInitialY(const int32_t initialY) noexcept; - bool GetIsInitialYSet() const noexcept; + bool IsInitialYSet() const noexcept; winrt::TerminalApp::LaunchMode GetLaunchMode() const noexcept; void SetLaunchMode(const winrt::TerminalApp::LaunchMode launchMode); @@ -86,8 +86,8 @@ class TerminalApp::GlobalAppSettings final int32_t _initialRows; int32_t _initialCols; - int32_t _initialX; - int32_t _initialY; + std::optional _initialX; + std::optional _initialY; bool _isInitialXSet; bool _isInitialYSet; From c700753b44d4513605e79ad709789ce668f2ecad Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Fri, 11 Oct 2019 12:29:05 -0700 Subject: [PATCH 21/25] Combine the initial X/Y position into one string property --- .../TerminalApp/GlobalAppSettings.cpp | 110 +++++++++++++++--- src/cascadia/TerminalApp/GlobalAppSettings.h | 11 ++ 2 files changed, 103 insertions(+), 18 deletions(-) diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 8e10e0bb929..f9b2c1eb6bb 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -7,6 +7,7 @@ #include "../../inc/DefaultSettings.h" #include "Utils.h" #include "JsonUtils.h" +#include using namespace TerminalApp; using namespace winrt::Microsoft::Terminal::Settings; @@ -20,8 +21,7 @@ static constexpr std::string_view DefaultProfileKey{ "defaultProfile" }; static constexpr std::string_view AlwaysShowTabsKey{ "alwaysShowTabs" }; static constexpr std::string_view InitialRowsKey{ "initialRows" }; static constexpr std::string_view InitialColsKey{ "initialCols" }; -static constexpr std::string_view InitialXKey{ "initialX" }; -static constexpr std::string_view InitialYKey{ "initialY" }; +static constexpr std::string_view InitialPositionKey{ "initialPosition" }; static constexpr std::string_view ShowTitleInTitlebarKey{ "showTerminalTitleInTitlebar" }; static constexpr std::string_view RequestedThemeKey{ "requestedTheme" }; static constexpr std::string_view ShowTabsInTitlebarKey{ "showTabsInTitlebar" }; @@ -215,14 +215,7 @@ Json::Value GlobalAppSettings::ToJson() const jsonObject[JsonKey(DefaultProfileKey)] = winrt::to_string(Utils::GuidToString(_defaultProfile)); jsonObject[JsonKey(InitialRowsKey)] = _initialRows; jsonObject[JsonKey(InitialColsKey)] = _initialCols; - if (_isInitialXSet) - { - jsonObject[JsonKey(InitialXKey)] = _initialX.value(); - } - if (_isInitialYSet) - { - jsonObject[JsonKey(InitialYKey)] = _initialY.value(); - } + jsonObject[JsonKey(InitialPositionKey)] = _SerializeInitialPosition(_initialX.value(), _isInitialXSet, _initialY.value(), _isInitialYSet); jsonObject[JsonKey(AlwaysShowTabsKey)] = _alwaysShowTabs; jsonObject[JsonKey(ShowTitleInTitlebarKey)] = _showTitleInTitlebar; jsonObject[JsonKey(ShowTabsInTitlebarKey)] = _showTabsInTitlebar; @@ -268,15 +261,9 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) { _initialCols = initialCols.asInt(); } - if (auto initialX{ json[JsonKey(InitialXKey)] }) + if (auto initialPosition{ json[JsonKey(InitialPositionKey)] }) { - _isInitialXSet = true; - _initialX = initialX.asInt(); - } - if (auto initialY{ json[JsonKey(InitialYKey)] }) - { - _isInitialYSet = true; - _initialY = initialY.asInt(); + _ParseInitialPosition(GetWstringFromJson(initialPosition), _initialX.value(), _isInitialXSet, _initialY.value(), _isInitialYSet); } if (auto showTitleInTitlebar{ json[JsonKey(ShowTitleInTitlebarKey)] }) { @@ -355,6 +342,93 @@ std::wstring_view GlobalAppSettings::_SerializeTheme(const ElementTheme theme) n } } +// Method Description: +// - Helper function for converting the initial position string into +// 2 coordinate values. We allow users to only provide one coordinate, +// thus, we use comma as the separater: +// (100, 100): standard input string +// (, 100), (100, ): if a value is missing, we set this value as a default +// (,): both x and y are set to default +// (abc, 100): if a value is not valid, we treat it as default +// (100, 100, 100): we only read the first two values, this is equivalent to (100, 100) +// Arguments: +// - initialPosition: the initial position string from json +// initialX: reference to the _initialX member +// isInitialXSet: reference to the _isInitialXSet member +// initialY: reference to the _initialY member +// isInitialYSet: reference to the _isInitialYSet member +// Return Value: +// - None +void GlobalAppSettings::_ParseInitialPosition(const std::wstring& initialPosition, + int32_t& initialX, + bool& isInitialXSet, + int32_t& initialY, + bool& isInitialYSet) noexcept +{ + const wchar_t singleCharDelim = L','; + std::wstringstream tokenStream(initialPosition.c_str()); + std::wstring token; + uint8_t initialPosIndex = 0; + size_t* idx = nullptr; + + // Get initial position values till we run out of delimiter separated values in the stream + // or we hit max number of allowable values (= 2) + // Non-numeral values or empty string will be caught as exception and we do not assign them + for (; std::getline(tokenStream, token, singleCharDelim) && (initialPosIndex < 2); initialPosIndex++) + { + try + { + int32_t position = std::stoi(token, idx); + if (initialPosIndex == 0) + { + initialX = position; + isInitialXSet = true; + } + + if (initialPosIndex == 1) + { + initialY = position; + isInitialYSet = true; + } + } + catch (...) + { + // Do nothing + } + } +} + +// Method Description: +// - Helper function for converting X/Y initial positions to a string +// value. +// Arguments: +// - initialX: reference to the _initialX member +// isInitialXSet: reference to the _isInitialXSet member +// initialY: reference to the _initialY member +// isInitialYSet: reference to the _isInitialYSet member +// Return Value: +// - The concatenated string for the the current initialX and initialY +std::string GlobalAppSettings::_SerializeInitialPosition(const int32_t& initialX, + const bool& isInitialXSet, + const int32_t& initialY, + const bool& isInitialYSet) noexcept +{ + std::string serializedInitialPos = "("; + if (isInitialXSet) + { + serializedInitialPos += initialX; + } + + serializedInitialPos += ", "; + + if (isInitialYSet) + { + serializedInitialPos += initialY; + } + + return serializedInitialPos; +} + // Method Description: // - Helper function for converting the user-specified launch mode // to a LaunchMode enum value diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.h b/src/cascadia/TerminalApp/GlobalAppSettings.h index be16a96932b..12f2697f50f 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.h +++ b/src/cascadia/TerminalApp/GlobalAppSettings.h @@ -105,6 +105,17 @@ class TerminalApp::GlobalAppSettings final static winrt::Windows::UI::Xaml::ElementTheme _ParseTheme(const std::wstring& themeString) noexcept; static std::wstring_view _SerializeTheme(const winrt::Windows::UI::Xaml::ElementTheme theme) noexcept; + static void _ParseInitialPosition(const std::wstring& initialPosition, + int32_t& initialX, + bool& isInitialXSet, + int32_t& initialY, + bool& isInitialYSet) noexcept; + + static std::string _SerializeInitialPosition(const int32_t& initialX, + const bool& isInitialXSet, + const int32_t& initialY, + const bool& isInitialYSet) noexcept; + static std::wstring_view _SerializeLaunchMode(const winrt::TerminalApp::LaunchMode launchMode) noexcept; static winrt::TerminalApp::LaunchMode _ParseLaunchMode(const std::wstring& launchModeString) noexcept; From bba2b9bb29e8915bfe542c33ee58c62c9a10ea9e Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Fri, 11 Oct 2019 13:08:21 -0700 Subject: [PATCH 22/25] Change the return type of AppHost::_HandleCreateWindow back to void --- src/cascadia/WindowsTerminal/AppHost.cpp | 12 ++++++------ src/cascadia/WindowsTerminal/AppHost.h | 2 +- src/cascadia/WindowsTerminal/IslandWindow.cpp | 4 ++-- src/cascadia/WindowsTerminal/IslandWindow.h | 5 ++--- src/cascadia/WindowsTerminal/pch.h | 1 + 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index d282897b98d..76b96055d19 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -33,7 +33,8 @@ AppHost::AppHost() noexcept : auto pfn = std::bind(&AppHost::_HandleCreateWindow, this, std::placeholders::_1, - std::placeholders::_2); + std::placeholders::_2, + std::placeholders::_3); _window->SetCreateCallback(pfn); _window->MakeWindow(); @@ -125,11 +126,12 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*se // - proposedRect: The location and size of the window that we're about to // create. We'll use this rect to determine which monitor the window is about // to appear on. +// - launchMode: A LaunchMode enum reference that indicates the launch mode // Return Value: -// - A LaunchMode enum that indicates the launch mode -winrt::TerminalApp::LaunchMode AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) +// - None +void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect, winrt::TerminalApp::LaunchMode& launchMode) { - winrt::TerminalApp::LaunchMode launchMode = _app.GetLaunchMode(); + launchMode = _app.GetLaunchMode(); // Acquire the actual intial position winrt::Windows::Foundation::Point initialPosition = _app.GetLaunchInitialPositions(proposedRect.left, proposedRect.top); @@ -260,8 +262,6 @@ winrt::TerminalApp::LaunchMode AppHost::_HandleCreateWindow(const HWND hwnd, REC TraceLoggingDescription("Event emitted upon creating the application window"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); - - return launchMode; } // Method Description: diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index d41ad8008d3..1712098b665 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -24,7 +24,7 @@ class AppHost std::unique_ptr _window; winrt::TerminalApp::App _app; - winrt::TerminalApp::LaunchMode _HandleCreateWindow(const HWND hwnd, RECT proposedRect); + void _HandleCreateWindow(const HWND hwnd, RECT proposedRect, winrt::TerminalApp::LaunchMode& launchMode); void _UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::UIElement& arg); void _UpdateTheme(const winrt::TerminalApp::App&, diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index f7a2c0f9a39..86044470365 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -88,7 +88,7 @@ void IslandWindow::Close() // window. // Return Value: // - -void IslandWindow::SetCreateCallback(std::function pfn) noexcept +void IslandWindow::SetCreateCallback(std::function pfn) noexcept { _pfnCreateCallback = pfn; } @@ -113,7 +113,7 @@ void IslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexce winrt::TerminalApp::LaunchMode launchMode = winrt::TerminalApp::LaunchMode::DefaultMode; if (_pfnCreateCallback) { - launchMode = _pfnCreateCallback(_window.get(), rc); + _pfnCreateCallback(_window.get(), rc, launchMode); } int nCmdShow = SW_SHOW; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 316bce74b51..b7850c9e379 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -8,7 +8,6 @@ #include #include #include "../../cascadia/inc/cppwinrt_utils.h" -#include class IslandWindow : public BaseWindow, @@ -33,7 +32,7 @@ class IslandWindow : virtual void Initialize(); - void SetCreateCallback(std::function pfn) noexcept; + void SetCreateCallback(std::function pfn) noexcept; void UpdateTheme(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme); @@ -86,7 +85,7 @@ class IslandWindow : winrt::Windows::UI::Xaml::Controls::Grid _rootGrid; - std::function _pfnCreateCallback; + std::function _pfnCreateCallback; void _HandleCreateWindow(const WPARAM wParam, const LPARAM lParam) noexcept; }; diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index b6d9e77abdc..509e941a2b0 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -28,6 +28,7 @@ Module Name: #include #include #include +#include #include "../inc/LibraryIncludes.h" From 929e8aef8979be2f6a04e881ec283fe34f8d4989 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Fri, 11 Oct 2019 17:57:54 -0700 Subject: [PATCH 23/25] Elinamate useless bools for initialX/Y --- src/cascadia/TerminalApp/App.cpp | 10 ++-- .../TerminalApp/GlobalAppSettings.cpp | 54 ++++++------------- src/cascadia/TerminalApp/GlobalAppSettings.h | 18 ++----- 3 files changed, 27 insertions(+), 55 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 85c201632d2..4a5572fcd1f 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -382,13 +382,15 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Foundation::Point point((float)defaultInitialX, (float)defaultInitialY); - if (_settings->GlobalSettings().IsInitialXSet()) + auto initialX = _settings->GlobalSettings().GetInitialX(); + auto initialY = _settings->GlobalSettings().GetInitialY(); + if (initialX.has_value()) { - point.X = gsl::narrow_cast(_settings->GlobalSettings().GetInitialX().value()); + point.X = gsl::narrow_cast(initialX.value()); } - if (_settings->GlobalSettings().IsInitialYSet()) + if (initialY.has_value()) { - point.Y = gsl::narrow_cast(_settings->GlobalSettings().GetInitialY().value()); + point.Y = gsl::narrow_cast(initialY.value()); } return point; diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index f9b2c1eb6bb..ad7e26d79d5 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -41,10 +41,8 @@ GlobalAppSettings::GlobalAppSettings() : _alwaysShowTabs{ true }, _initialRows{ DEFAULT_ROWS }, _initialCols{ DEFAULT_COLS }, - _initialX{ 0 }, - _initialY{ 0 }, - _isInitialXSet{ false }, - _isInitialYSet{ false }, + _initialX{}, + _initialY{}, _showTitleInTitlebar{ true }, _showTabsInTitlebar{ true }, _requestedTheme{ ElementTheme::Default }, @@ -161,12 +159,7 @@ std::optional GlobalAppSettings::GetInitialX() const noexcept void GlobalAppSettings::SetInitialX(const int32_t initialX) noexcept { - _initialX = initialX; -} - -bool GlobalAppSettings::IsInitialXSet() const noexcept -{ - return _isInitialXSet; + _initialX.value() = initialX; } std::optional GlobalAppSettings::GetInitialY() const noexcept @@ -176,12 +169,7 @@ std::optional GlobalAppSettings::GetInitialY() const noexcept void GlobalAppSettings::SetInitialY(const int32_t initialY) noexcept { - _initialY = initialY; -} - -bool GlobalAppSettings::IsInitialYSet() const noexcept -{ - return _isInitialYSet; + _initialY.value() = initialY; } #pragma endregion @@ -215,7 +203,7 @@ Json::Value GlobalAppSettings::ToJson() const jsonObject[JsonKey(DefaultProfileKey)] = winrt::to_string(Utils::GuidToString(_defaultProfile)); jsonObject[JsonKey(InitialRowsKey)] = _initialRows; jsonObject[JsonKey(InitialColsKey)] = _initialCols; - jsonObject[JsonKey(InitialPositionKey)] = _SerializeInitialPosition(_initialX.value(), _isInitialXSet, _initialY.value(), _isInitialYSet); + jsonObject[JsonKey(InitialPositionKey)] = _SerializeInitialPosition(_initialX, _initialY); jsonObject[JsonKey(AlwaysShowTabsKey)] = _alwaysShowTabs; jsonObject[JsonKey(ShowTitleInTitlebarKey)] = _showTitleInTitlebar; jsonObject[JsonKey(ShowTabsInTitlebarKey)] = _showTabsInTitlebar; @@ -263,7 +251,7 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) } if (auto initialPosition{ json[JsonKey(InitialPositionKey)] }) { - _ParseInitialPosition(GetWstringFromJson(initialPosition), _initialX.value(), _isInitialXSet, _initialY.value(), _isInitialYSet); + _ParseInitialPosition(GetWstringFromJson(initialPosition), _initialX, _initialY); } if (auto showTitleInTitlebar{ json[JsonKey(ShowTitleInTitlebarKey)] }) { @@ -354,16 +342,12 @@ std::wstring_view GlobalAppSettings::_SerializeTheme(const ElementTheme theme) n // Arguments: // - initialPosition: the initial position string from json // initialX: reference to the _initialX member -// isInitialXSet: reference to the _isInitialXSet member // initialY: reference to the _initialY member -// isInitialYSet: reference to the _isInitialYSet member // Return Value: // - None void GlobalAppSettings::_ParseInitialPosition(const std::wstring& initialPosition, - int32_t& initialX, - bool& isInitialXSet, - int32_t& initialY, - bool& isInitialYSet) noexcept + std::optional& initialX, + std::optional& initialY) noexcept { const wchar_t singleCharDelim = L','; std::wstringstream tokenStream(initialPosition.c_str()); @@ -381,14 +365,12 @@ void GlobalAppSettings::_ParseInitialPosition(const std::wstring& initialPositio int32_t position = std::stoi(token, idx); if (initialPosIndex == 0) { - initialX = position; - isInitialXSet = true; + initialX.emplace(position); } if (initialPosIndex == 1) { - initialY = position; - isInitialYSet = true; + initialY.emplace(position); } } catch (...) @@ -403,27 +385,23 @@ void GlobalAppSettings::_ParseInitialPosition(const std::wstring& initialPositio // value. // Arguments: // - initialX: reference to the _initialX member -// isInitialXSet: reference to the _isInitialXSet member // initialY: reference to the _initialY member -// isInitialYSet: reference to the _isInitialYSet member // Return Value: // - The concatenated string for the the current initialX and initialY -std::string GlobalAppSettings::_SerializeInitialPosition(const int32_t& initialX, - const bool& isInitialXSet, - const int32_t& initialY, - const bool& isInitialYSet) noexcept +std::string GlobalAppSettings::_SerializeInitialPosition(const std::optional& initialX, + const std::optional& initialY) noexcept { std::string serializedInitialPos = "("; - if (isInitialXSet) + if (initialX.has_value()) { - serializedInitialPos += initialX; + serializedInitialPos += initialX.value(); } serializedInitialPos += ", "; - if (isInitialYSet) + if (initialY.has_value()) { - serializedInitialPos += initialY; + serializedInitialPos += initialY.value(); } return serializedInitialPos; diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.h b/src/cascadia/TerminalApp/GlobalAppSettings.h index 12f2697f50f..32eea39058e 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.h +++ b/src/cascadia/TerminalApp/GlobalAppSettings.h @@ -60,11 +60,9 @@ class TerminalApp::GlobalAppSettings final std::optional GetInitialX() const noexcept; void SetInitialX(const int32_t initialX) noexcept; - bool IsInitialXSet() const noexcept; std::optional GetInitialY() const noexcept; void SetInitialY(const int32_t initialY) noexcept; - bool IsInitialYSet() const noexcept; winrt::TerminalApp::LaunchMode GetLaunchMode() const noexcept; void SetLaunchMode(const winrt::TerminalApp::LaunchMode launchMode); @@ -88,8 +86,6 @@ class TerminalApp::GlobalAppSettings final std::optional _initialX; std::optional _initialY; - bool _isInitialXSet; - bool _isInitialYSet; bool _showStatusline; bool _alwaysShowTabs; @@ -106,15 +102,11 @@ class TerminalApp::GlobalAppSettings final static std::wstring_view _SerializeTheme(const winrt::Windows::UI::Xaml::ElementTheme theme) noexcept; static void _ParseInitialPosition(const std::wstring& initialPosition, - int32_t& initialX, - bool& isInitialXSet, - int32_t& initialY, - bool& isInitialYSet) noexcept; - - static std::string _SerializeInitialPosition(const int32_t& initialX, - const bool& isInitialXSet, - const int32_t& initialY, - const bool& isInitialYSet) noexcept; + std::optional& initialX, + std::optional& initialY) noexcept; + + static std::string _SerializeInitialPosition(const std::optional& initialX, + const std::optional& initialY) noexcept; static std::wstring_view _SerializeLaunchMode(const winrt::TerminalApp::LaunchMode launchMode) noexcept; static winrt::TerminalApp::LaunchMode _ParseLaunchMode(const std::wstring& launchModeString) noexcept; From 1cf8f8379abadad17df6dffc22819d4ca0be9e5c Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Mon, 14 Oct 2019 10:34:46 -0700 Subject: [PATCH 24/25] remove useless setters in GlobalAppSettings --- src/cascadia/TerminalApp/GlobalAppSettings.cpp | 10 ---------- src/cascadia/TerminalApp/GlobalAppSettings.h | 2 -- 2 files changed, 12 deletions(-) diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index ad7e26d79d5..77c11837945 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -157,21 +157,11 @@ std::optional GlobalAppSettings::GetInitialX() const noexcept return _initialX; } -void GlobalAppSettings::SetInitialX(const int32_t initialX) noexcept -{ - _initialX.value() = initialX; -} - std::optional GlobalAppSettings::GetInitialY() const noexcept { return _initialY; } -void GlobalAppSettings::SetInitialY(const int32_t initialY) noexcept -{ - _initialY.value() = initialY; -} - #pragma endregion // Method Description: diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.h b/src/cascadia/TerminalApp/GlobalAppSettings.h index 32eea39058e..3072f9a67e9 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.h +++ b/src/cascadia/TerminalApp/GlobalAppSettings.h @@ -59,10 +59,8 @@ class TerminalApp::GlobalAppSettings final void SetCopyOnSelect(const bool copyOnSelect) noexcept; std::optional GetInitialX() const noexcept; - void SetInitialX(const int32_t initialX) noexcept; std::optional GetInitialY() const noexcept; - void SetInitialY(const int32_t initialY) noexcept; winrt::TerminalApp::LaunchMode GetLaunchMode() const noexcept; void SetLaunchMode(const winrt::TerminalApp::LaunchMode launchMode); From e39b11017221253bea0ee04fd1457f3d8df22ec0 Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Tue, 15 Oct 2019 10:37:21 -0700 Subject: [PATCH 25/25] Remove useless variables in GlobalAppSettings --- src/cascadia/TerminalApp/GlobalAppSettings.cpp | 7 +++---- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 77c11837945..ea0029063c7 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -340,10 +340,9 @@ void GlobalAppSettings::_ParseInitialPosition(const std::wstring& initialPositio std::optional& initialY) noexcept { const wchar_t singleCharDelim = L','; - std::wstringstream tokenStream(initialPosition.c_str()); + std::wstringstream tokenStream(initialPosition); std::wstring token; uint8_t initialPosIndex = 0; - size_t* idx = nullptr; // Get initial position values till we run out of delimiter separated values in the stream // or we hit max number of allowable values (= 2) @@ -352,7 +351,7 @@ void GlobalAppSettings::_ParseInitialPosition(const std::wstring& initialPositio { try { - int32_t position = std::stoi(token, idx); + int32_t position = std::stoi(token); if (initialPosIndex == 0) { initialX.emplace(position); @@ -381,7 +380,7 @@ void GlobalAppSettings::_ParseInitialPosition(const std::wstring& initialPositio std::string GlobalAppSettings::_SerializeInitialPosition(const std::optional& initialX, const std::optional& initialY) noexcept { - std::string serializedInitialPos = "("; + std::string serializedInitialPos = ""; if (initialX.has_value()) { serializedInitialPos += initialX.value(); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 76b96055d19..28ec1857d64 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -227,7 +227,7 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect, winrt::Ter // coincides with the screen const auto pCWindow = static_cast(_window.get()); const RECT frame = pCWindow->GetFrameBorderMargins(dpix); - proposedRect.left -= (frame.left * -1); + proposedRect.left += frame.left; } adjustedHeight = nonClient.bottom - nonClient.top;