From b31cb2fd4a7f7f380ea367d6ea8c7bf1e96ae7a4 Mon Sep 17 00:00:00 2001 From: elParaguayo Date: Tue, 7 May 2024 22:04:00 +0100 Subject: [PATCH] Add border decorations --- CHANGELOG | 1 + .../images/matrix_screen_gradient_1.png | Bin 0 -> 13948 bytes .../images/matrix_screen_gradient_2.png | Bin 0 -> 16822 bytes .../images/matrix_screen_gradient_3.png | Bin 0 -> 23408 bytes docs/_static/images/max_gradient_border.png | Bin 0 -> 11793 bytes docs/_static/images/max_gradient_border_2.png | Bin 0 -> 13849 bytes docs/_static/images/max_gradient_frame.png | Bin 0 -> 10242 bytes docs/index.rst | 3 + docs/manual/how_to/borders.rst | 51 +++ docs/manual/ref/borders.rst | 20 + qtile_extras/layout/__init__.py | 20 + qtile_extras/layout/decorations/__init__.py | 52 +++ qtile_extras/layout/decorations/borders.py | 357 ++++++++++++++++++ qtile_extras/layout/decorations/injections.py | 187 +++++++++ test/layout/__init__.py | 0 test/layout/decorations/__init__.py | 0 .../decorations/test_border_decorations.py | 61 +++ 17 files changed, 752 insertions(+) create mode 100644 docs/_static/images/matrix_screen_gradient_1.png create mode 100644 docs/_static/images/matrix_screen_gradient_2.png create mode 100644 docs/_static/images/matrix_screen_gradient_3.png create mode 100644 docs/_static/images/max_gradient_border.png create mode 100644 docs/_static/images/max_gradient_border_2.png create mode 100644 docs/_static/images/max_gradient_frame.png create mode 100644 docs/manual/how_to/borders.rst create mode 100644 docs/manual/ref/borders.rst create mode 100644 qtile_extras/layout/__init__.py create mode 100644 qtile_extras/layout/decorations/__init__.py create mode 100644 qtile_extras/layout/decorations/borders.py create mode 100644 qtile_extras/layout/decorations/injections.py create mode 100644 test/layout/__init__.py create mode 100644 test/layout/decorations/__init__.py create mode 100644 test/layout/decorations/test_border_decorations.py diff --git a/CHANGELOG b/CHANGELOG index bf91673a..4ab2a476 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +2024-05-09: [FEATURE] Add window border decorations (NB experimental) 2024-04-26: [FEATURE] Add hooks to `ALSAWidget` and `PulseVolumeExtra` 2024-04-25: [FEATURE] Add ability to drag `PopupSlider` and `PopupCircularProgress` controls (NB experimental) 2024-04-25: [BREAKING CHANGE] Update to using lazy calls in popups. Needs latest qtile. diff --git a/docs/_static/images/matrix_screen_gradient_1.png b/docs/_static/images/matrix_screen_gradient_1.png new file mode 100644 index 0000000000000000000000000000000000000000..2be835fa52dafbef345b4140718df3da006e3753 GIT binary patch literal 13948 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 O;4BW9=kGcB`~LtLC?Qz@ literal 0 HcmV?d00001 diff --git a/docs/_static/images/matrix_screen_gradient_2.png b/docs/_static/images/matrix_screen_gradient_2.png new file mode 100644 index 0000000000000000000000000000000000000000..4091763dead88e669dceff482c6875344706f5f3 GIT binary patch literal 16822 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

lcmYAltQ@?o z9D@H+7|%ZnWB+Hs>~PNb*N8>f|6e*0L0<45Ne0~a_Z&!GAQ!U#I~o3?GdS)1|M>Th zE&hLO0f_$JM*c_o{a5 zAfK+#oDc+2LGq6tYI;v>O#7shc?GOo9W^xhZDncGPS~bM{o6H^y<}3~u@COfDu*&)$`!N}HyOl}Nge#wkxlJ%2l;V?5|nQWOqLZE9Vi`h`DH zIk$Y}lcotpyh?`v8-rO@dXJL$o`+K6Om2}eNnK`Z2hW->MR8U6LcYn#Ri2JY?;oGi zFCv;Q1|#O8$3w@h?H&31ISoV>vtjbBt@Zn>=wG#_Nw6K4WSYt@5d2Vtwm7`F&LA>ze7f#rQ+H zCMmrVi^@0RW^Qe6KC8KbCAF^;GA~{RM3JTlIe%C3hPj-_R*(>2r8ib06`UxJMxroz=D{ zy=s@XKl;=nUt1`ndLZpaM?yJ&+i#~fbHsW0l4(w|^5Tb-@Ua*_N_t=N&jjhOe&_o^)W>J{(%I_Lr}9UhywbaAF;>%e-c`CX)HT{GZkv+g!mQi&cz^TYBt|!> zeEX-bu25;o_(0`LU9l%ASFTySlLMZ=zTDw07bPGY#7E?-l#hHKtUn$o>hx^4xJbJA z&{^71y>Gm+JZPcYPZU;zqcphPNFJE@Q};;9bB0K?;5Yfn_iJyz?Y|ywAZwWV@L;(m zR7Ko1_ii2c-5QNMr|+j!ZRe%@6x*SYl_vdp^1Sc8+5f6@H0(VMo@--`-7|YbZ}#e)0MpM`Xkr=&Qh* z#x{h??o;*cyI6*pkdF6~dnDiNE|Wvsl5c9a28p1LOx)w%e!IHW1zr*nXA$=yR-&1A zm`-iF>ufVcMY#jkrXmd8g`07tBVHy2H*wN+&e?{QC;YqY>V9?dKgb~c{{7{!R>c#Q zUIIK@9q|VzL!a0Q_RNPj<96Q_{W2g4OaZyr>M)5i)zYD5{cytn(4xnackSR4EthhJ z&~{?Q@Ny)-t@EtdlI2u=t;Xc2b*=DIf$vA(GxJtM`KKpxz0P|?*Vg4^w{UB{^ox^c zcPORaziV<;gAI_=)P&_9>oP#te#~*P>{(>vc{~6!~)9 zx7k|el4nXc8w_ck94~<{obi243&;r=Q(o)2ZEL)3s2sh5vE7!dO4NGpnBmonx!k4b zRj#3U)Gnf$IbQKX+*@7PXxGw0n55mP_hBOio?YFrDgx5kTw@hiax19UcTWxdBc97G z8?DH-7Vs0Og?LzVjG$@5LYt1AyuE4OBZ_9$=p1kPgPmdNQzMr&#MWpAHMjkb=1abo z(ht_z-7ti)c(?-0e%ho0XT3?O5^&mR5 z-Z>EL!QRsDl}>(7`I*VaJyYc_RqOWpH{-UU)K^L_boX*cNBIaSLzNo-{O+r0R{q6+ z=KhP>sI2W`O+!dTM1x1=Mg1T9(Il+oBH{R?!GxQtmLqRe?3{glvxPRA9`hS{zs|o% z`yoDE`r7JJoq<7RuMxFhl79MaeZNW+xj0>3+9I#z$S7a-moKm0y$sn@J!(|Qi(cOy zKxl1}ZYOfKo>C&@q{Q74u>;o?g%Iv?aR=v_vOF$Jb+0RubsID?T8N{zoxZ!bKK%KI zK4U9OrR{VXdb&QBnPJYh&Lfob?)?S$SLq zUKl7J#wfuyS)mzv!oq=8Qn$0O(?hjV^BeA*y-WcT~C z*Fo;(%E^aoR*ZqBYT}pQvev%7bg8T5w_7X_|9*6JfG6a^HEzr<&Pg}mUR>ss`=+Vc zQnsHtxwp_`TFBxJMwjvV)GH+!TVbos4I4Y7VKen!LDC=JC*GcaWsJ%cDwD!NXsG&c zIeDBr+xW$&tB6#}t~<6W0`2sR?Dzz%cTf(#9AwoS<0$(Trb<*Ft~@<)k<`G{u#LzN z5MmqP=2|7J^K4+wu+iFo^GEWlyZzwEw*T1%F?sHCooG*OZDDrN!^R@UC7G|PE?<3N>vnTvLa$KpN9`=r7sZwX}zVPw#NfM;YusLYnd^5j z-s)sG@S1_w(Ea2E-BSJ;FNa|E%a^}|{7>In`oAH*X61sOvrBu%^TtWbt)`-5_Si~v zcgcw{3)7Kk@7IeSuZ)uY@}GIjGOJ%-;8^>cOXlXXPIyi#j&Rf&T@ORcOGQ_%FB?W> zZ`6_!_j!c(YnVh7p%=j3s91(o;J#PIz`I;F^5?1Jk2ie=LWjT2-eh7Me&O*j%^I34 zAH2`*_p-WWz4lKJY3)UmV(nFyc;2tGQ%usSQA3BORa*;w`{5?c{=p(yio{uuD$ll4 zdLKirKGt}llh1B@ZW?chNk>*!u$i*>_5AjGJ-W#oN_YcxVlJoKY)$8aRYWgSZboCv z3l(>Wd&{)?E61qXE&LXXI*9pdr<;pubYf%cLoZ2ap>cdmL*xHX2WL0E=7i4w2zgJ}nt6|09X(9~kqEH}0zLCL`@Csa?Ywd07T0in_q_k%`fM zhMOO&xicBW^TX(dV@F2X4sKo?Y>VZ6Qebp2baT2D-!UrsMHuj6%g7`X=qDNlZTHArM&DRG+PZozyKN}&Ea3~=wKGG+F zwmLY0YZ^VON}9)}oKL6EYi0h3<+u1zK216z<{GshFnsbR%=PN&^V`(oP^lhxra;Ig zFOLZMZ6r^HJ2RTslquwCoPL$P)QtNvJY2;8;gZ*X2l}Du=>I%Kttdy)JNXw4J?Np9 zclbAoGKGPVZu^JoC-3~8CI*<)$g~bsoIg6ocjFc$7w}F$grE|Mx_(3~B~4UqBpM<{)Xv`Ah|58{}!cfhs zur>bvTvH}BGvUk8iq1G_U$7P(jCjtTO$>wdN`ltCfse}a(%n}=QN|8&=&vkVYt#{= zR8@&$T(=~f+SZK&Nsz7ZQB~pT57QiqSKv{KGiANdhAMr3yf?TBa)6a=kY)?%%=ss7 zq1{j1uM0H%XUCFQ(+=lx0wm%R6bcg3sBIwQyd7nSh&!;le7u7_I-a%=gWxU9`I^D2Uv(Y*%$@7OGIjp22mMu zu)M#43C-yTAK&+p1P}j}7`XD8PM_(z0VzT8wL8TcMFs4(=~{W zjqfXR)o>5~q_d#%8wz5=r!h2lxdsJEnShmtKC(4MZwQLRCnbA-4SJ7Hxwk@+jYk6o zy+2oTchWw1jtW&Gn935&SK%jfYJkj zrLdRmN)Qy@p79+8atyiEi3WWX6ltFb!liD^*9_K@u6|yMGYmWu(%6Jl=--I zf@%wkR4!?>5HJAxM|3`&oybE1tA`L&^>CgYpW3|x6RPaR8N_0T-nLWSncleu#c@S) zp+Z4A6p;`#_q+ylMWLPcI~F@Ir86TKb0s!TTP@bD$eWOwN~7>w4PYFWj$l5}4`zhR z;6q1XBT2dqu%?5UdO;C}DJ+}HrRt$4Fm8w09|V;?tZ)rIf3<*}2~egg{PNJm8h zt3`@uu%{whamKm>d*JxeO;mK~Er#`V&^q&@Z}yy~4**!IpMR_w^gSFGQnPsojC}I; zmd$lZV3{5+9tku^Q!m^Zoe+Ycp!=U;eW@t)zjMRIgp7$P?xTM7&y|Frg?&(G(y#+B zJtguwyUyJffr=vL;g19ucL*^ds{EalU(zDLxC_rg2aR>CT~Oc_^dqsV;RvA-gAI-z z83VcAfQ%VIdt*R%`j~gHL0IXxn`J}P;QMG`Cuns3@dOq-ynZuLv|hv@-ff^ z1brr;>P$9#3<_I{AICxv?gOsBHVvx2jRks+e*N78p5LKFkZ~tFn~=5eEl4h0IQu%p z#HVOIs_$P;W&vQ)j6l* z51gBX0(s*XHK6$^%sa5qxJB6=C!hp1tw;PHV5bDp?t#22j=WQMJ|47CK*gBOwE{B> za=hJp0Afy|lS6aCX?LzcQ_pGd5#oc0`3&2AH>YX-z9w+i5IK3zSZXNfc;~@X<0viA zLw)0&Hv}MCe&hpDc>`q4hX@vtF5o!{STS>1aDWur!b()z!54yKvN(wq_{wg?K)5pr zIfSci4%_{st`7f)8h-j4Ah^Z9-UgW!z>`HW(0l~u-F{H4WH0psm@?>*be9MhNba~4 z&$y|`K3#(nq=BCVVRXEJqfP64!8Et_p%5{w%s9zhH#n1yQ!_)*6ENL@LkxXTWC3g!ix74x%3wUygpT^l{0H42;;DTdf(BxNS3d4?bx+j_DmnI~@Er zA2P-$$bI)WYj46iwjY~$%EIuWe7=SwLh?%6F_X2c=+A*_v$CJdRx*J)~T!+g>eVq;KehK7tHX3yvS@4R6nGp50B^xEY zY-Rj5`U4Zkc5OHeugqxdMESr#_54ENxRdoPeG?j3Rd0`L*~xZ(&0?0Y3{xhGu~IU$ zS;}b5dilJ9qQFRYUvvG$E7h^g(>|`hO`apbj5FXDM*tq5%I5=S@_Ki&UmT@5IG>_F>!m+@=0&^6GK}(OrDx?3DnqO7e&kot z?-Dxwby?cTkLf~Zr4@z(-rQoLEo^fK4e!1D?O6&4*>s-Bi9hj3HjACnoIoQ#bJd^o>I=Q$7#>H0bQK~)l{8^+cpWUOPe86-5;UGhmd3s)+{ddBUB{6#^^UO{5 z{k&M-EJPIh6aC#yJ2Ej$Mc#md0V~mZarG$nNQ3$0qtRbptuqa}C+R#tbiJp}Y2;Jy zU-oOS*5LjmEXWj-sGE5(WbNw1-(>alEc|mXIEBYM^IEQUxa#@D;-Z_n+0#nWnGYgM zORSD1OGK=Z3x6CRw-F5SfjIbxk`rfgZF5ushb+o;zimickk{N!WEbR0Td6DW1d6rc!~=PD&5DGJ+4_wzX^`XIR^N<5oJX@$TuAw+~J z8&Pj^Jed%b?B+R7xsop9HfuzbjEWlQH{V{Lv5G@b7gxS6vKwn%G9nt5G zw_T{&eD+p@Nff9vidGnh=`4t}lnoi4f;hh@eyVX>cEMyI=?oF`eAMi0X=sdRQefcJk(o_L9bre4 zePyz_;l(jJ5GgeI(&hMhro+)i;rl0^qSHsT*5>O`LRFrA-uxc~i8D&Wo=yz0P(W*{ zpimxeuNUC$Nf_qkO?4hn$^B4Ln9a^^XNJ$PJL{ z!9znQDmq#fm~%z4z>_rH1EC!7T>e(y8-7ImoByAB!^?-D;yrbRhI*#yAC(jPFP`6k zzQ#Oy7)LJwy(M||hHrByPyDLJmTy=+U)~H2Q7C#8@Hif?@x(;kLDzz0_>CBM;gr&R zJ_7JVO}#g%DHvI<`SPiw^zuG^x3QSBl7}2N_l9IHf4tA{qNiP992qJ-KTh#C;#!sA zb+47@#(P6Tf;EL--`;=?>2#h@|6&~e$rzvZ{l|A5>CP~^ywSXaPsv9LnXjXln-5x2 z^VitNG-b4n+_P`0h*zYBr~M`we(ToMP@Gq!ow3S@rK-H=bEDGq-Pp$Wy6xi_6930=LYVa6yk?+}5RYE% zcCEg1ydz?ElK0Q}5swH@Q8csD@O`hDayKLQ8j>$rPp$^33yxBnJtP}hU+e|gY&cmN z{ql%YS6?=N_Wx-7$9C@w9qvb|q&o zF>4Nv&Z)$qSjygX%KV3E6|>p2SLgDge?Gcoc%6A{IS54feb}=i&1PT}%g?9h72?7_ zCng`w@!sQVT92w2>s)af8EBiw<}FZ2_RB4AS?p`FJj0s!>6JRltB5t7T*w@cetYzL zt+JQCUxlInZsbVA(nwQILD~RIkkD)OvqoJkk=<8-MJ&#}8=ZAedoxVCmUZxBa9F5$ zzR$+eT-~lB@Fl&<*5L2&3HdSN#aaGKkC!4V!rr9*IPOODDG_0x#E?(qzILx*^Brz> z@9rb|nng0&O?x)tw#3>rzu8v2oRmOkq)~vz>vH;S!%o>|J0P%PettJ(Gth9YdDU^V zsa~~|vD=S-@`afbVa{%y%?Yy>@RUvz;EO~t^t$wZI>d44$nt*?8t?a&R)j6Z6$0G5e zyVKAvEe`WYlkUkU1|9)!ynCgMoBm6x0g52iH977JVZBn_Nkvs-a3z@#&)LEpi02d} zKR(F%y;DT!FcXmJOR_J-%^}p>u~D>=uf2Ni7KJz;%hF(5Au4`;GCUiySs*T2{Hy>` zyjs*=*VZ9^{DI=KSM~Q`g;>#+!DL?d9nr(FD-H3>$hPmAOeLfC@pMMN!j4uCK56Vv zx{7ZPIL2cR^U@bwzd9urJ#O5#8+KriOXiSZS`AGGsz^+tY< zle!rbtJCkw(3@TNee~Qmqb6ccid01R2~G@O)VFO>WYl*^#g>@;$1JsFF1y$h)iVz| zy30;GUiObe#H8d}c2Ao}FwMDa`jiH~X#oJJq%b_p9T~d+iedK1;??WzmE?0FGpNwn ztvH{xx$bPmFk2>2p@+wIVJFx3V7u*Y_wG-J%w#Q%Z9SjN*RFUZ;+#~ocA9QZ4X-$R zqHf#neM_OEt)R|5*UZ+A*YsDj&V5poJnKpgXqB<-4#pXm_8p&2bYF=Tz9h|z7pFaJ`>>-w z)+R^KTIcUqLJ}AW++-w$5>c${5U`#|x;5r~MZ9udr(<&K`^!4vJblB{m;J{nGMLoRoA3U8|544~ly?d9s=p-~J<1@62bVpUdm)Z_kjUv^@j`;CAn z%dx(r`Y+iS);FX2-g4@W))H2sHzO`ZbE~Azo=l*<%t~XMh(3Emu`zaFJO}|0E8?0@ z_)|ANTInkev?!agD2q_)7}zY?aUe8!zLXp+S9a*2Um$+Iv%UK9*^7`KmJD@A`huem zM_woAC^{#5+;519bz8RC-;L`OPhYl1tsgX=T`H^|NYd_~wH-RNoQVg-4v<58x#f1H z#?Ar_C@&MaxRhlst&c}czT6`haCUB;8?W|hnK`j*`xb@`X-S4A;L|>_cul$L4h-!} z{aM{c3j->xl%#Xv|M2*OGWo=@xM#`I^V^ujP=7QFz`xL+$uo&qoma-M9_MF2l(IU- zjJog_--#FJdG|V3eY@r960b`Q1K2E<4r}{9%np`};aD+`yd<5ht<^Q$(!R*Z^6&|L z*x}}*S=tlY*Y;a-5|!Od=C7^GpPj1k!S&&RIKr zE)Cf48oL->(b{y_Vhr~Bjvt~hwL{}|$Gag$ho2t|VMyPNff7WZ>3__LD$fdNSmoZ6 zHZXUlTVgNMT#1T^6i+F!eLviaR~74f0}}8@c8wGkX~fi3nG;&mOu*t`mkuMOb`()Q zdANcRdw;{;eC~jQpZrEpqK<#c&dRH#Wy|2}1!0Y=R8Z+Pz;oOlw(kK>L`Wkhx(p~n zJMH*iq6kyea2xyD>I^YX0Ad07}qVk+lJfdZ;H< zc83#25HH<=L1XV1ZUeE{(%?bzu8D($001`5tO_uabJ*oH3Q_}fiEKZ2hd&ZvmwOa2 z&yh=@MN~fZ0u1!p=l#x~U_7YR*B~{MU26H&aDaawBvWDr0=yJdO~dvHrt)-O?*Nke z{U(qQtmuUTZOxlRG|lH>N<$JMsQ@;VeY~&ZUT_Q0Qa)IRj`Jo+?4CrK{g?N0$$j_= zXvRkvX?&z-A?y$Oo=5`)EdV79&pmLw-o60ZU=e`9$WlbKX)OvMhDh$Bfblw(j|ygA zbl(8qFKvGg6UYBf_hUT7KdXGG~x${=yHz6)3ad{iFqvttBWZ~`aq{>8o2ROJ3j_zS)nTJOM{82uq=u#1c^|7$=ilQ5qTdcW}&23~n4?f1cg zTG(WITRj}K6B*N}Sa}L;@Ge68LathnTu81iyx$-u&XYw60w%aik`xzku8+JL zIyWFl7HOwc(V(TUI@IjDU?(Kxt3D}i7J##w&t*VcGNKDWI<{gboCkT?Pp?wGR@)!G>qWUSppCsB%gF$!a`g3<6 z;E?y*6eB3bQv@y~U zykUUk+#0$e3%eB(hsE=Y)JlH4ux`q1ONp zw4tsR0Q^>mB?S-m8s`UMw=f|IRN}w9Tu7h?pAI&M&K}^h@MZ=W80@oj1JM$2p9Bbi zKE^G(Hk^=WZ{q{}9wZzC^aVi@%F-Skz0W}EQ4Iff)i($Z?jx^a2Yyu9hdnFDg{01E z{>Q+ULFVuAgdi#(YDs`toxfqjh?Wk%()k-Me5`30`~4O<*oGL#B+226R?j!A4|QTZ0rKTTYIm*Q8v{o9S}`|z=PO@ zkm$D?3%RMJ&?)Qk9tcgN4u#EC|LKl8`28r54{y6UBb(})x&<-;@ZirmzyX5XJa1qk zN2tlJ55n*vyAEL05x1j(k?iroh(KPHK$S)>7({_wI%NkRmLB+rg0149@#jd|jluN@ zX%uEWLj3z+HzX5OaY0Os3IY+RP`E|{!Ri7$SqO>Lz8|0a3MLV-m)CkjQGW>7m+=vt zNzr|t-JS=t2G!$-$Soxc>8}~zuY)Z#X!MyRaOg@|*tlKKfhz?QFOb4k8^0+#=p?=n z2!?P=|9Ph_;LCm{lHMQ+mV&`v1?U|#hRjpAy*ChkogwHTS4iKY2FmZj=_90)=C8Uh z4ieC(s(bLxoD17C3lx$!?9U@ATu}wIY z0L#G;J%|_h)?6$B(sK;$;aSKOwdOgj_oPnQ#+(1=?dPkgMR`g1a>7l4#JlRrqUj45Vj&0Z`cF7mK=NV*)37OU8$EECA&x-#Ux!^Hy18#vp&v_`b9FqQT2^k zp!n`ULOND(0@q`+RRm0Wx!t1@qGq&{_zXgmus%V>s#4vgF1`=C6tskl_ZKw6L zX#Ebozt-?l%xykdF#fL3mk##w=Q;daZfymTU$W}StOi5*)&iM8U4hFr6fL3Bo*R;l zOWOjaeN75H(Vdmq>x~D3X&e>K7hgghLOPmW)hrG_K^0c?z%}Nbd@%NqqdfFT%dx9= zCh4IV!%6Eat3{`N1^R{3_cZ-niI?n-`rVExk1CwF5qZOfLw=R1U2n?5nEK5`ze!Cs z6(6o}x{_E-Xr2&u=mcod=9`EHbRFr`*nB>p^-2kQbk&g3YBcXxnHlEFJe401Vl+>> zOP;^2IKh4PInS-&qy3kP1rY%!9$QWeUVH!R9J5R9Jp zv<6j%)%~sR4iw1OlPi!Uw6gb!JaI?YDiaGVncv52aMb>O^xjf>?ylqT`jX#vRSjd+2%(fvIG&yO%KHEFWaUMnpz!DUwQd!q4u~ixkf_J1P}d z4HoY`P9_L=S3L>}{ohNe<47+=i0L);v~t%?n8^FqYO4FXY587d#3f@afBuQ+mKmzi zt<$^d;Uce+VMz02)5L+T>S_OWr^4B*ku$%NgP{_IHsRiyXNt$DS$-m{t5#>29akMB zj1F2Whsn%`6GPP~^|!~c$JXk!Ck}fRPkwr|%p{N}PS|VPwU9AQKh}*tOdF7oX?tR0 zhS4e8Q!+19-u2~MXQxFXY5ahZNBUcn3Zpz1rFIjdC_7I39|s2c>s--Q#G~JgPBumC z-x|jp8;Gb@lXclNS$siH3!F45;SET$uoYIZ=_0PvY3rQ*HDpp;)lW#i=tZ2rMPl(3 zTlna%jYca})J4Ih<%>i5GT)EzP(nQ>D$1bM=&=4=IVwNPeTAx9C2F`*K0nL2AeSUt zyDTGmA!Af;yD&v^g>rR)o8jV@ormc=hh1HQu9=OboXP1*m!z`E=^LV(f_+}!xXRy8 z8jMlkFmIr)xn59$7lOzrWCBPe#!8%z2CL2hCU8pPP>N4X)@jTbHVfe?}`bMl~(<2f0!1 zB%zpHWEwPOm5r!83^B1KRcR&5^txp{=EtUKhh`e$(fst3qDv!yWN&ofis3uf_RH6N z7acPyC_&6-CuCZcSB{p=69!XQPWx_AJ*{mPiR_U0vJ)gx7-}@A)**?qPz7dbm9tr~ zy!#N9d^A4Wr@gPBHe|sx7#BRZ5K*G0gSkN9!C25O@UXtIbB$Os8dUTg=lr}5rgbt- zu;+_b394RDstX0cD&n~;m`aNc!IM7jYXTdvoGSjTSmM!(eP~#ze5uo>B66AX<))05 zNzgHP_?JM?F^OG6l`G@5v6bjCWAC#G z!3d&DeOWf1ujRmMq`6Uwf#SRxTeMoWmGVlDO>m(*p>f?Q@!eCugJ`2h*#Pp#htZGy zy7GKau(k)M$6B8@WO-3j%0SEt|F;tLr1EU#)q~jR4Z*ht6#*rB)x>#zUaP2W2bK

RyVA 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 literal 0 HcmV?d00001 diff --git a/docs/index.rst b/docs/index.rst index a4daa4cb..2b6b1e58 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -22,6 +22,7 @@ There's a :ref:`mixin ` if you want to add tooltips to widgets. I've also added some "eye candy" in the form of: - :ref:`Widget Decorations ` + - :ref:`Window Border Decorations ` - :ref:`Wallpapers ` Lastly, I've created a new ``ImgMask`` class which, rather than drawing the source image, @@ -44,6 +45,7 @@ without needing to recrate the icons themselves. You can see the class :ref:`her manual/install manual/how_to/popup manual/how_to/decorations + manual/how_to/borders manual/how_to/img-mask manual/how_to/tooltips @@ -56,6 +58,7 @@ without needing to recrate the icons themselves. You can see the class :ref:`her manual/ref/hooks manual/ref/popup manual/ref/decorations + manual/ref/borders manual/ref/imgmask .. toctree:: diff --git a/docs/manual/how_to/borders.rst b/docs/manual/how_to/borders.rst new file mode 100644 index 00000000..70a757f5 --- /dev/null +++ b/docs/manual/how_to/borders.rst @@ -0,0 +1,51 @@ +.. _border-decorations: + +========================= +Window Border Decorations +========================= + +.. warning:: + + This feature is experimental. + + The decorations may behave unexpectedly, have missing features and will + probably crash at some point! + + Feedback on any issues would be appreciated. + +Window border decorations provide the ability to have different style borders +rather than the standard single, solid colour borders. + +The following decorations are available: + +.. list_objects:: qtile_extras.layout.decorations + :baseclass: qtile_extras.layout.decorations.borders._BorderStyle + +Using the decorations is simple: + +.. code:: python + + from qtile_extras.layout.decorations import GradientBorder + + ... + + layouts = [ + layout.Max( + border_width=10, + margin=5, + border_focus=GradientBorder(colours=["00f", "0ff"]) + ), + ... + ] + +Results in a window looking like this: + +.. image:: /_static/images/max_gradient_border.png + +See :ref:`this page` for details of the various borders available. + +.. important:: + + You must import the decorations from ``qtile_extras.layout.decorations`` as importing + this file will add a hook to inject the code needed to allow qtile to render these + borders. diff --git a/docs/manual/ref/borders.rst b/docs/manual/ref/borders.rst new file mode 100644 index 00000000..4e8a72e3 --- /dev/null +++ b/docs/manual/ref/borders.rst @@ -0,0 +1,20 @@ +.. _ref-borders: + +========================= +Window Border Decorations +========================= + +.. warning:: + + This feature is experimental. + + The decorations may behave unexpectedly, have missing features and will + probably crash at some point! + + Feedback on any issues would be appreciated. + +.. qtile_module:: qtile_extras.layout.decorations.borders + :baseclass: qtile_extras.layout.decorations.borders._BorderStyle + :exclude-base: + :no-commands: + :show-config: diff --git a/qtile_extras/layout/__init__.py b/qtile_extras/layout/__init__.py new file mode 100644 index 00000000..25bc2016 --- /dev/null +++ b/qtile_extras/layout/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2024 elParaguayo +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from libqtile.layout import * # noqa: F401, F403 diff --git a/qtile_extras/layout/decorations/__init__.py b/qtile_extras/layout/decorations/__init__.py new file mode 100644 index 00000000..7bac7666 --- /dev/null +++ b/qtile_extras/layout/decorations/__init__.py @@ -0,0 +1,52 @@ +# Copyright (c) 2024 elParaguayo +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from libqtile import hook + +from qtile_extras.layout.decorations.borders import ( # noqa: F401 + GradientBorder, + GradientFrame, + ScreenGradientBorder, +) + + +# We need to inject code into qtile to allow the windows to render the new +# decorations. To simplify this, we can use a hook so it's invisible to +# users. +@hook.subscribe.startup_once +def inject_border_methods(): + from libqtile import qtile + + if qtile.core.name == "wayland": + from libqtile.backend.wayland.window import Window + + from qtile_extras.layout.decorations.injections import ( + wayland_paint_borders, + wayland_window_init, + ) + + Window.__init__ = wayland_window_init + Window.paint_borders = wayland_paint_borders + + else: + from libqtile.backend.x11.window import XWindow + + from qtile_extras.layout.decorations.injections import x11_paint_borders + + XWindow.paint_borders = x11_paint_borders diff --git a/qtile_extras/layout/decorations/borders.py b/qtile_extras/layout/decorations/borders.py new file mode 100644 index 00000000..e101ba04 --- /dev/null +++ b/qtile_extras/layout/decorations/borders.py @@ -0,0 +1,357 @@ +# Copyright (c) 2024 elParaguayo +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +import cairocffi +from libqtile import qtile +from libqtile.configurable import Configurable +from libqtile.confreader import ConfigError +from libqtile.utils import rgb + +try: + from libqtile.backend.wayland._ffi import ffi, lib + from wlroots import ffi as wlr_ffi + from wlroots import lib as wlr_lib + from wlroots.wlr_types import Buffer, SceneBuffer + + HAS_WAYLAND = True +except (ImportError, ModuleNotFoundError): + HAS_WAYLAND = False + +HALF_ROOT_2 = 0.70711 + + +class _BorderStyle(Configurable): + """ + Base class for border decorations. Should be instantiated directly. + + This class is responsible for initialising the objects required to generate + more complext borders. + + Decorations that render their design to a surface can just use the ``draw`` + method for this. + """ + + needs_surface = True + + def _create_xcb_surface(self): + root = self.window.conn.conn.get_setup().roots[0] + + def find_visual(visual): + for d in root.allowed_depths: + if d.depth == self.depth: + for v in d.visuals: + if v.visual_id == visual: + return v + + return cairocffi.XCBSurface( + self.window.conn.conn, + self.pixmap, + find_visual(self.visual), + self.outer_w, + self.outer_h, + ) + + def _new_buffer(self): + surface = cairocffi.ImageSurface(cairocffi.FORMAT_ARGB32, self.outer_w, self.outer_h) + stride = surface.get_stride() + data = cairocffi.cairo.cairo_image_surface_get_data(surface._pointer) + image_buffer = lib.cairo_buffer_create(self.outer_w, self.outer_h, stride, data) + if image_buffer == ffi.NULL: + raise RuntimeError("Couldn't allocate cairo buffer.") + + return image_buffer, surface + + def _x11_draw( + self, window, depth, pixmap, gc, outer_w, outer_h, borderwidth, x, y, width, height + ): + self.visual = window.get_attributes().visual + self.window = window + self.wid = window.wid + self.depth = depth + self.pixmap = pixmap + self.gc = gc + self.outer_w = outer_w + self.outer_h = outer_h + if self.needs_surface: + surface = self._create_xcb_surface() + else: + surface = None + self.x11_draw(borderwidth, x, y, width, height, surface) + + def x11_draw(self, borderwidth, x, y, width, height, surface): + self.draw(surface, borderwidth, x, y, width, height) + + def _wayland_draw(self, window, outer_w, outer_h, borderwidth, x, y, width, height): + if not HAS_WAYLAND: + raise ConfigError("Unable to load wayland backend during imports.") + self.window = window + self.wid = window.wid + self.outer_w = outer_w + self.outer_h = outer_h + bw = borderwidth + self.rects = [ + (x, y, width, bw), + (outer_w - bw - x, bw + y, bw, height - 2 * bw), + (x, outer_h - y - bw, width, bw), + (x, bw + y, bw, height - bw * 2), + ] + if self.needs_surface: + image_buffer, surface = self._new_buffer() + else: + image_buffer = None + surface = None + + self.wayland_draw(borderwidth, x, y, width, height, surface) + + scenes = [] + for x, y, w, h in self.rects: + scene_buffer = SceneBuffer.create(self.window.container, Buffer(image_buffer)) + scene_buffer.node.set_position(x, y) + wlr_lib.wlr_scene_buffer_set_dest_size(scene_buffer._ptr, w, h) + fbox = wlr_ffi.new("struct wlr_fbox *") + fbox.x = x + fbox.y = y + fbox.width = w + fbox.height = h + wlr_lib.wlr_scene_buffer_set_source_box(scene_buffer._ptr, fbox) + scenes.append(scene_buffer) + + return scenes, image_buffer, surface + + def wayland_draw(self, borderwidth, x, y, width, height, surface): + self.draw(surface, borderwidth, x, y, width, height) + + def draw(self, surface, bw, x, y, width, height): + pass + + +class GradientBorder(_BorderStyle): + """ + Renders borders with a gradient. + + ``colours`` defines the list of colours in the gradient. + + The angle/direction of the gradient is set by the ``points`` + parameter. This is a list of a two (x, y) tuples. The x and y + values are relative to the window. A value of (0, 0) is the top + left corner while (1, 1) represents the bottom right corner. + + ``offsets`` is used to adjust the position of the colours within + the gradient. Leaving this as ``None`` will space the colours evenly. The + values need to be in ascending order and in the range of 0.0 (the very start of + the gradient) and 1.0 (the end of the gradient). These represent positions on the + imagninary line between the two ``points`` defined above. + + When ``radial=True`` the ``points`` parameter has no impact. The gradient will be drawn from the center + of the window to the corner of the window. ``offsets`` can still be used to adjust the + spacing of the colours. + """ + + defaults = [ + ("colours", ["00ffff", "0000ff"], "List of colours in the gradient"), + ("points", [(0, 0), (0, 1)], "Points to size/angle the gradient. See docs for more."), + ( + "offsets", + None, + "Offset locations (in range of 0.0-1.0) for gradient stops. ``None`` to use regular spacing.", + ), + ("radial", False, "Use radial gradient"), + ] + + _screenshots = [ + ("max_gradient_border.png", 'border_focus=GradientBorder(colours=["00f", "0ff"])'), + ( + "max_gradient_border_2.png", + 'border_focus=GradientBorder(colours=["f0f", "00f", "0ff"], points=[(0, 1), (1, 0)])', + ), + ] + + def __init__(self, **config): + _BorderStyle.__init__(self, **config) + self.add_defaults(GradientBorder.defaults) + + if self.offsets is None: + self.offsets = [x / (len(self.colours) - 1) for x in range(len(self.colours))] + elif len(self.offsets) != len(self.colours): + raise ConfigError("'offsets' must be same length as 'colours'.") + + def draw(self, surface, bw, x, y, width, height): + def pos(point): + return tuple(p * d for p, d in zip(point, (width, height))) + + with cairocffi.Context(surface) as ctx: + ctx.save() + ctx.translate(x, y) + + # Use winding rules to clip an area equal to the borders + ctx.rectangle(0, 0, width, height) + ctx.rectangle(width - bw, bw, -(width - 2 * bw), (height - 2 * bw)) + ctx.clip() + + if self.radial: + ctx.translate(width // 2, height // 2) + ctx.scale(width, height) + gradient = cairocffi.RadialGradient(0, 0, 0, 0, 0, HALF_ROOT_2) + else: + gradient = cairocffi.LinearGradient(*pos(self.points[0]), *pos(self.points[1])) + + for offset, c in zip(self.offsets, self.colours): + gradient.add_color_stop_rgba(offset, *rgb(c)) + + ctx.set_source(gradient) + ctx.paint() + ctx.restore() + + +class GradientFrame(_BorderStyle): + """ + Renders a frame with a gradient. Each edge's gradient is from the outside towards the centre. + """ + + defaults = [ + ("colours", ["00ffff", "0000ff"], "List of colours in the gradient"), + ] + + _screenshots = [ + ("max_gradient_frame.png", 'border_focus=GradientFrame(colours=["00f", "0ff"])'), + ] + + def __init__(self, **config): + _BorderStyle.__init__(self, **config) + self.add_defaults(GradientFrame.defaults) + self.offsets = [x / (len(self.colours) - 1) for x in range(len(self.colours))] + + def draw(self, surface, bw, x, y, width, height): + with cairocffi.Context(surface) as ctx: + ctx.save() + ctx.translate(x, y) + + edges = [ + ([(0, 0), (width, 0), (width - bw, bw), (bw, bw)], (0, 0, 0, bw)), + ( + [(width, 0), (width, height), (width - bw, height - bw), (width - bw, bw)], + (width, 0, width - bw, 0), + ), + ( + [(0, height), (bw, height - bw), (width - bw, height - bw), (width, height)], + (0, height, 0, height - bw), + ), + ([(0, 0), (bw, bw), (bw, height - bw), (0, height)], (0, 0, bw, 0)), + ] + + for points, grad_points in edges: + ctx.new_path() + ctx.move_to(*points.pop(0)) + for p in points: + ctx.line_to(*p) + ctx.close_path() + ctx.clip() + gradient = cairocffi.LinearGradient(*grad_points) + for offset, c in zip(self.offsets, self.colours): + gradient.add_color_stop_rgba(offset, *rgb(c)) + ctx.set_source(gradient) + ctx.paint() + ctx.reset_clip() + ctx.restore() + + +class ScreenGradientBorder(GradientBorder): + """ + Renders a border with a gradient which is scaled to the screen, rather than the window. + This means that a window's border will change depending on where it is in the screen. + + ``colours`` defines the list of colours in the gradient. + + The angle/direction of the gradient is set by the ``points`` + parameter. This is a list of a two (x, y) tuples. The x and y + values are relative to the screen. A value of (0, 0) is the top + left corner while (1, 1) represents the bottom right corner. + + ``offsets`` is used to adjust the position of the colours within + the gradient. Leaving this as ``None`` will space the colours evenly. The + values need to be in ascending order and in the range of 0.0 (the very start of + the gradient) and 1.0 (the end of the gradient). These represent positions on the + imagninary line between the two ``points`` defined above. + + When ``radial=True`` the ``points`` parameter has no impact. The gradient will be drawn from the center + of the screen to the corner of the screen. ``offsets`` can still be used to adjust the + spacing of the colours. + """ + + _to_add = """ + Setting ``per_screen=False`` will render the gradient across all monitors with the behaviour of + ``points`` being adjusted to represent the full screen area covered by all monitors. + """ + defaults = [ + # ("per_screen", True, "Whether gradient is redrawn per screen. If False, gradient spans all monitors."), + ] + + _screenshots = [ + ( + "matrix_screen_gradient_1.png", + 'ScreenGradientBorder(colours=["f00", "0f0", "00f"], points=[(0,0), (1,1)])', + ), + ("matrix_screen_gradient_2.png", "Gradient is applied to screen..."), + ("matrix_screen_gradient_3.png", "...no matter how many windows are open."), + ] + + def __init__(self, **config): + GradientBorder.__init__(self, **config) + self.add_defaults(ScreenGradientBorder.defaults) + + def draw(self, surface, bw, x, y, width, height): + assert qtile is not None + win = qtile.windows_map.get(self.wid) + if win and win.group and win.group.screen: + w = win.group.screen.width + h = win.group.screen.height + win_x = win.x + win_y = win.y + else: + w = width + h = height + win_x = win_y = 0 + + def pos(point): + return tuple( + (p * d) - n - m for p, d, n, m in zip(point, (w, h), (x, y), (win_x, win_y)) + ) + + with cairocffi.Context(surface) as ctx: + ctx.save() + ctx.translate(x, y) + + # Use winding rules to clip an area equal to the borders + ctx.rectangle(0, 0, width, height) + ctx.rectangle(width - bw, bw, -(width - 2 * bw), (height - 2 * bw)) + ctx.clip() + + if self.radial: + ctx.translate(w // 2 - x - win_x, h // 2 - y - win_y) + ctx.scale(w, h) + gradient = cairocffi.RadialGradient(0, 0, 0, 0, 0, HALF_ROOT_2) + else: + gradient = cairocffi.LinearGradient(*pos(self.points[0]), *pos(self.points[1])) + + for offset, c in zip(self.offsets, self.colours): + gradient.add_color_stop_rgba(offset, *rgb(c)) + + ctx.set_source(gradient) + ctx.paint() + ctx.restore() diff --git a/qtile_extras/layout/decorations/injections.py b/qtile_extras/layout/decorations/injections.py new file mode 100644 index 00000000..355951a7 --- /dev/null +++ b/qtile_extras/layout/decorations/injections.py @@ -0,0 +1,187 @@ +# Copyright (c) 2021 Matt Colligan +# Copyright (c) 2024 elParaguayo +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +from typing import TYPE_CHECKING + +import xcffib +from libqtile.backend.wayland.window import SceneRect, Window, _rgb +from xcffib.wrappers import GContextID, PixmapID + +from qtile_extras.layout.decorations.borders import _BorderStyle + +if TYPE_CHECKING: + from libqtile.backend.wayland.window import Core, Qtile, S + from libqtile.utils import ColorsType + + +old_wayland_window_init = Window.__init__ + + +def wayland_window_init(self, core: Core, qtile: Qtile, surface: S): + old_wayland_window_init(self, core, qtile, surface) + self._border_styles = {} + + +def wayland_paint_borders(self, colors: ColorsType | None, width: int) -> None: + if not colors: + colors = [] + width = 0 + + if not isinstance(colors, list): + colors = [colors] + + if self.tree: + self.tree.node.set_position(width, width) + self.bordercolor = colors + self.borderwidth = width + + if width == 0: + for rects in self._borders: + for rect in rects: + rect.node.destroy() + self._borders.clear() + return + + if len(colors) > width: + colors = colors[:width] + + num = len(colors) + old_borders = self._borders + new_borders = [] + widths = [width // num] * num + for i in range(width % num): + widths[i] += 1 + + outer_w = self.width + width * 2 + outer_h = self.height + width * 2 + coord = 0 + + for i, color in enumerate(colors): + bw = widths[i] + if isinstance(color, _BorderStyle): + scenes, image_buffer, surface = color._wayland_draw( + self, + outer_w, + outer_h, + bw, + coord, + coord, + outer_w - coord * 2, + outer_h - coord * 2, + ) + new_borders.append(scenes) + self._border_styles[color] = (scenes, image_buffer, surface) + else: + color_ = _rgb(color) + + # [x, y, width, height] for N, E, S, W + geometries = ( + (coord, coord, outer_w - coord * 2, bw), + (outer_w - bw - coord, bw + coord, bw, outer_h - bw * 2 - coord * 2), + (coord, outer_h - bw - coord, outer_w - coord * 2, bw), + (coord, bw + coord, bw, outer_h - bw * 2 - coord * 2), + ) + + if old_borders: + rects = old_borders.pop(0) + for (x, y, w, h), rect in zip(geometries, rects): + rect.set_color(color_) + rect.set_size(w, h) + rect.node.set_position(x, y) + + else: + rects = [] + for x, y, w, h in geometries: + rect = SceneRect(self.container, w, h, color_) + rect.node.set_position(x, y) + rects.append(rect) + + new_borders.append(rects) + coord += bw + + for rects in old_borders: + for rect in rects: + rect.node.destroy() + + # Ensure the window contents and any nested surfaces are drawn above the + # borders. + if self.tree: + self.tree.node.raise_to_top() + + self._borders = new_borders + + +def x11_paint_borders(self, depth, colors, borderwidth, width, height): + """ + This method is used only by the managing Window class. + """ + self.set_property("_NET_FRAME_EXTENTS", [borderwidth] * 4) + + if not colors or not borderwidth: + return + + if isinstance(colors, str): + self.set_attribute(borderpixel=self.conn.color_pixel(colors)) + return + elif isinstance(colors, _BorderStyle): + colors = [colors] + + if len(colors) > borderwidth: + colors = colors[:borderwidth] + core = self.conn.conn.core + outer_w = width + borderwidth * 2 + outer_h = height + borderwidth * 2 + + with PixmapID(self.conn.conn) as pixmap: + with GContextID(self.conn.conn) as gc: + core.CreatePixmap(depth, pixmap, self.wid, outer_w, outer_h) + core.CreateGC(gc, pixmap, 0, None) + borders = len(colors) + borderwidths = [borderwidth // borders] * borders + for i in range(borderwidth % borders): + borderwidths[i] += 1 + coord = 0 + for i in range(borders): + if isinstance(colors[i], _BorderStyle): + colors[i]._x11_draw( + self, + depth, + pixmap, + gc, + outer_w, + outer_h, + borderwidth, + coord, + coord, + outer_w - coord * 2, + outer_h - coord * 2, + ) + else: + core.ChangeGC( + gc, xcffib.xproto.GC.Foreground, [self.conn.color_pixel(colors[i])] + ) + rect = xcffib.xproto.RECTANGLE.synthetic( + coord, coord, outer_w - coord * 2, outer_h - coord * 2 + ) + core.PolyFillRectangle(pixmap, gc, 1, [rect]) + coord += borderwidths[i] + self._set_borderpixmap(depth, pixmap, gc, borderwidth, width, height) diff --git a/test/layout/__init__.py b/test/layout/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/layout/decorations/__init__.py b/test/layout/decorations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/layout/decorations/test_border_decorations.py b/test/layout/decorations/test_border_decorations.py new file mode 100644 index 00000000..dc5b2992 --- /dev/null +++ b/test/layout/decorations/test_border_decorations.py @@ -0,0 +1,61 @@ +# Copyright (c) 2024 elParaguayo +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +import pytest +from libqtile.config import Screen +from libqtile.confreader import Config +from libqtile.layout import Matrix + +from qtile_extras.layout.decorations import GradientBorder, GradientFrame, ScreenGradientBorder + + +@pytest.fixture +def manager(request, manager_nospawn): + class BorderDecorationConfig(Config): + decoration = getattr(request, "param", None) + if decoration is None: + raise ValueError("No border decoration provided") + + layouts = [Matrix(border_focus=decoration)] + screens = [Screen()] + + manager_nospawn.start(BorderDecorationConfig) + + yield manager_nospawn + + +@pytest.mark.parametrize( + "manager", + [ + GradientFrame(), + GradientFrame(colours=["f00", "0f0", "00f"]), + GradientBorder(), + GradientBorder(colours=["f00", "0f0", "00f"]), + GradientBorder(colours=["f00", "0f0", "00f"], points=[(0, 0), (1, 0)]), + GradientBorder(colours=["f00", "0f0", "00f"], offsets=[0, 0.1, 1]), + ScreenGradientBorder(), + ScreenGradientBorder(colours=["f00", "0f0", "00f"], points=[(0, 0), (1, 0)]), + ScreenGradientBorder(colours=["f00", "0f0", "00f"], offsets=[0, 0.1, 1]), + ], + indirect=True, +) +def test_window_decoration(manager): + manager.test_window("one") + manager.test_window("two") + assert True