diff --git a/bmps/times-new-roman/#1.bmp b/bmps/times-new-roman/#1.bmp new file mode 100644 index 0000000..c4bebdb Binary files /dev/null and b/bmps/times-new-roman/#1.bmp differ diff --git a/bmps/times-new-roman/%1.bmp b/bmps/times-new-roman/%1.bmp new file mode 100644 index 0000000..76f5b1a Binary files /dev/null and b/bmps/times-new-roman/%1.bmp differ diff --git a/bmps/times-new-roman/&1.bmp b/bmps/times-new-roman/&1.bmp new file mode 100644 index 0000000..c01ebf1 Binary files /dev/null and b/bmps/times-new-roman/&1.bmp differ diff --git a/bmps/times-new-roman/'0.bmp b/bmps/times-new-roman/'0.bmp new file mode 100644 index 0000000..54e1333 Binary files /dev/null and b/bmps/times-new-roman/'0.bmp differ diff --git a/bmps/times-new-roman/+2.bmp b/bmps/times-new-roman/+2.bmp new file mode 100644 index 0000000..2edb95f Binary files /dev/null and b/bmps/times-new-roman/+2.bmp differ diff --git a/bmps/times-new-roman/;1.bmp b/bmps/times-new-roman/;1.bmp new file mode 100644 index 0000000..9448781 Binary files /dev/null and b/bmps/times-new-roman/;1.bmp differ diff --git a/bmps/times-new-roman/<2.bmp b/bmps/times-new-roman/<2.bmp new file mode 100644 index 0000000..286c6c4 Binary files /dev/null and b/bmps/times-new-roman/<2.bmp differ diff --git a/bmps/times-new-roman/>2.bmp b/bmps/times-new-roman/>2.bmp new file mode 100644 index 0000000..3b5f7da Binary files /dev/null and b/bmps/times-new-roman/>2.bmp differ diff --git a/bmps/times-new-roman/@0.bmp b/bmps/times-new-roman/@0.bmp new file mode 100644 index 0000000..c4850c8 Binary files /dev/null and b/bmps/times-new-roman/@0.bmp differ diff --git a/bmps/times-new-roman/[0.bmp b/bmps/times-new-roman/[0.bmp new file mode 100644 index 0000000..a815fb1 Binary files /dev/null and b/bmps/times-new-roman/[0.bmp differ diff --git a/bmps/times-new-roman/]0.bmp b/bmps/times-new-roman/]0.bmp new file mode 100644 index 0000000..fe64dec Binary files /dev/null and b/bmps/times-new-roman/]0.bmp differ diff --git a/bmps/times-new-roman/^0.bmp b/bmps/times-new-roman/^0.bmp new file mode 100644 index 0000000..edc74e4 Binary files /dev/null and b/bmps/times-new-roman/^0.bmp differ diff --git a/bmps/times-new-roman/_9.bmp b/bmps/times-new-roman/_9.bmp new file mode 100644 index 0000000..131939f Binary files /dev/null and b/bmps/times-new-roman/_9.bmp differ diff --git a/bmps/times-new-roman/`0.bmp b/bmps/times-new-roman/`0.bmp new file mode 100644 index 0000000..dbbd47b Binary files /dev/null and b/bmps/times-new-roman/`0.bmp differ diff --git a/bmps/times-new-roman/|0.bmp b/bmps/times-new-roman/|0.bmp new file mode 100644 index 0000000..365983d Binary files /dev/null and b/bmps/times-new-roman/|0.bmp differ diff --git a/bmps/times-new-roman/~5.bmp b/bmps/times-new-roman/~5.bmp new file mode 100644 index 0000000..742ea57 Binary files /dev/null and b/bmps/times-new-roman/~5.bmp differ diff --git a/bmps/times-new-romono/!0.bmp b/bmps/times-new-romono/!0.bmp new file mode 100644 index 0000000..1a31840 Binary files /dev/null and b/bmps/times-new-romono/!0.bmp differ diff --git "a/bmps/times-new-romono/\"0.bmp" "b/bmps/times-new-romono/\"0.bmp" new file mode 100644 index 0000000..45b4972 Binary files /dev/null and "b/bmps/times-new-romono/\"0.bmp" differ diff --git a/bmps/times-new-romono/#1.bmp b/bmps/times-new-romono/#1.bmp new file mode 100644 index 0000000..c4bebdb Binary files /dev/null and b/bmps/times-new-romono/#1.bmp differ diff --git a/bmps/times-new-romono/$2.bmp b/bmps/times-new-romono/$2.bmp new file mode 100644 index 0000000..b8c3b7c Binary files /dev/null and b/bmps/times-new-romono/$2.bmp differ diff --git a/bmps/times-new-romono/%1.bmp b/bmps/times-new-romono/%1.bmp new file mode 100644 index 0000000..76f5b1a Binary files /dev/null and b/bmps/times-new-romono/%1.bmp differ diff --git a/bmps/times-new-romono/&1.bmp b/bmps/times-new-romono/&1.bmp new file mode 100644 index 0000000..c01ebf1 Binary files /dev/null and b/bmps/times-new-romono/&1.bmp differ diff --git a/bmps/times-new-romono/'0.bmp b/bmps/times-new-romono/'0.bmp new file mode 100644 index 0000000..54e1333 Binary files /dev/null and b/bmps/times-new-romono/'0.bmp differ diff --git a/bmps/times-new-romono/(0.bmp b/bmps/times-new-romono/(0.bmp new file mode 100644 index 0000000..07a6a62 Binary files /dev/null and b/bmps/times-new-romono/(0.bmp differ diff --git a/bmps/times-new-romono/)0.bmp b/bmps/times-new-romono/)0.bmp new file mode 100644 index 0000000..ef5edf7 Binary files /dev/null and b/bmps/times-new-romono/)0.bmp differ diff --git a/bmps/times-new-romono/*0.bmp b/bmps/times-new-romono/*0.bmp new file mode 100644 index 0000000..05f21b4 Binary files /dev/null and b/bmps/times-new-romono/*0.bmp differ diff --git a/bmps/times-new-romono/+2.bmp b/bmps/times-new-romono/+2.bmp new file mode 100644 index 0000000..2edb95f Binary files /dev/null and b/bmps/times-new-romono/+2.bmp differ diff --git a/bmps/times-new-romono/,9.bmp b/bmps/times-new-romono/,9.bmp new file mode 100644 index 0000000..949d3e9 Binary files /dev/null and b/bmps/times-new-romono/,9.bmp differ diff --git a/bmps/times-new-romono/-8.bmp b/bmps/times-new-romono/-8.bmp new file mode 100644 index 0000000..6631a22 Binary files /dev/null and b/bmps/times-new-romono/-8.bmp differ diff --git a/bmps/times-new-romono/.9.bmp b/bmps/times-new-romono/.9.bmp new file mode 100644 index 0000000..665aada Binary files /dev/null and b/bmps/times-new-romono/.9.bmp differ diff --git a/bmps/times-new-romono/00.bmp b/bmps/times-new-romono/00.bmp new file mode 100644 index 0000000..d8958fe Binary files /dev/null and b/bmps/times-new-romono/00.bmp differ diff --git a/bmps/times-new-romono/10.bmp b/bmps/times-new-romono/10.bmp new file mode 100644 index 0000000..22a4f15 Binary files /dev/null and b/bmps/times-new-romono/10.bmp differ diff --git a/bmps/times-new-romono/20.bmp b/bmps/times-new-romono/20.bmp new file mode 100644 index 0000000..ba52be3 Binary files /dev/null and b/bmps/times-new-romono/20.bmp differ diff --git a/bmps/times-new-romono/30.bmp b/bmps/times-new-romono/30.bmp new file mode 100644 index 0000000..605f798 Binary files /dev/null and b/bmps/times-new-romono/30.bmp differ diff --git a/bmps/times-new-romono/40.bmp b/bmps/times-new-romono/40.bmp new file mode 100644 index 0000000..d6782f2 Binary files /dev/null and b/bmps/times-new-romono/40.bmp differ diff --git a/bmps/times-new-romono/50.bmp b/bmps/times-new-romono/50.bmp new file mode 100644 index 0000000..6f33bc6 Binary files /dev/null and b/bmps/times-new-romono/50.bmp differ diff --git a/bmps/times-new-romono/60.bmp b/bmps/times-new-romono/60.bmp new file mode 100644 index 0000000..8bb175e Binary files /dev/null and b/bmps/times-new-romono/60.bmp differ diff --git a/bmps/times-new-romono/70.bmp b/bmps/times-new-romono/70.bmp new file mode 100644 index 0000000..3ab4e73 Binary files /dev/null and b/bmps/times-new-romono/70.bmp differ diff --git a/bmps/times-new-romono/80.bmp b/bmps/times-new-romono/80.bmp new file mode 100644 index 0000000..d2a0f31 Binary files /dev/null and b/bmps/times-new-romono/80.bmp differ diff --git a/bmps/times-new-romono/90.bmp b/bmps/times-new-romono/90.bmp new file mode 100644 index 0000000..cc6a555 Binary files /dev/null and b/bmps/times-new-romono/90.bmp differ diff --git a/bmps/times-new-romono/:4.bmp b/bmps/times-new-romono/:4.bmp new file mode 100644 index 0000000..fe83551 Binary files /dev/null and b/bmps/times-new-romono/:4.bmp differ diff --git a/bmps/times-new-romono/;1.bmp b/bmps/times-new-romono/;1.bmp new file mode 100644 index 0000000..9448781 Binary files /dev/null and b/bmps/times-new-romono/;1.bmp differ diff --git a/bmps/times-new-romono/<2.bmp b/bmps/times-new-romono/<2.bmp new file mode 100644 index 0000000..286c6c4 Binary files /dev/null and b/bmps/times-new-romono/<2.bmp differ diff --git a/bmps/times-new-romono/>2.bmp b/bmps/times-new-romono/>2.bmp new file mode 100644 index 0000000..3b5f7da Binary files /dev/null and b/bmps/times-new-romono/>2.bmp differ diff --git a/bmps/times-new-romono/?0.bmp b/bmps/times-new-romono/?0.bmp new file mode 100644 index 0000000..cfb9e10 Binary files /dev/null and b/bmps/times-new-romono/?0.bmp differ diff --git a/bmps/times-new-romono/@0.bmp b/bmps/times-new-romono/@0.bmp new file mode 100644 index 0000000..c4850c8 Binary files /dev/null and b/bmps/times-new-romono/@0.bmp differ diff --git a/bmps/times-new-romono/A0.bmp b/bmps/times-new-romono/A0.bmp new file mode 100644 index 0000000..3fe298f Binary files /dev/null and b/bmps/times-new-romono/A0.bmp differ diff --git a/bmps/times-new-romono/B1.bmp b/bmps/times-new-romono/B1.bmp new file mode 100644 index 0000000..cc88669 Binary files /dev/null and b/bmps/times-new-romono/B1.bmp differ diff --git a/bmps/times-new-romono/C0.bmp b/bmps/times-new-romono/C0.bmp new file mode 100644 index 0000000..b677184 Binary files /dev/null and b/bmps/times-new-romono/C0.bmp differ diff --git a/bmps/times-new-romono/D1.bmp b/bmps/times-new-romono/D1.bmp new file mode 100644 index 0000000..64d9457 Binary files /dev/null and b/bmps/times-new-romono/D1.bmp differ diff --git a/bmps/times-new-romono/E1.bmp b/bmps/times-new-romono/E1.bmp new file mode 100644 index 0000000..41558b3 Binary files /dev/null and b/bmps/times-new-romono/E1.bmp differ diff --git a/bmps/times-new-romono/F1.bmp b/bmps/times-new-romono/F1.bmp new file mode 100644 index 0000000..86622f4 Binary files /dev/null and b/bmps/times-new-romono/F1.bmp differ diff --git a/bmps/times-new-romono/G0.bmp b/bmps/times-new-romono/G0.bmp new file mode 100644 index 0000000..600e6fd Binary files /dev/null and b/bmps/times-new-romono/G0.bmp differ diff --git a/bmps/times-new-romono/H1.bmp b/bmps/times-new-romono/H1.bmp new file mode 100644 index 0000000..5babed4 Binary files /dev/null and b/bmps/times-new-romono/H1.bmp differ diff --git a/bmps/times-new-romono/I1.bmp b/bmps/times-new-romono/I1.bmp new file mode 100644 index 0000000..c048c29 Binary files /dev/null and b/bmps/times-new-romono/I1.bmp differ diff --git a/bmps/times-new-romono/J0.bmp b/bmps/times-new-romono/J0.bmp new file mode 100644 index 0000000..08ea34e Binary files /dev/null and b/bmps/times-new-romono/J0.bmp differ diff --git a/bmps/times-new-romono/K1.bmp b/bmps/times-new-romono/K1.bmp new file mode 100644 index 0000000..4f87b99 Binary files /dev/null and b/bmps/times-new-romono/K1.bmp differ diff --git a/bmps/times-new-romono/L1.bmp b/bmps/times-new-romono/L1.bmp new file mode 100644 index 0000000..ebf012f Binary files /dev/null and b/bmps/times-new-romono/L1.bmp differ diff --git a/bmps/times-new-romono/M1.bmp b/bmps/times-new-romono/M1.bmp new file mode 100644 index 0000000..9b142be Binary files /dev/null and b/bmps/times-new-romono/M1.bmp differ diff --git a/bmps/times-new-romono/N0.bmp b/bmps/times-new-romono/N0.bmp new file mode 100644 index 0000000..9a73122 Binary files /dev/null and b/bmps/times-new-romono/N0.bmp differ diff --git a/bmps/times-new-romono/O0.bmp b/bmps/times-new-romono/O0.bmp new file mode 100644 index 0000000..e413880 Binary files /dev/null and b/bmps/times-new-romono/O0.bmp differ diff --git a/bmps/times-new-romono/P1.bmp b/bmps/times-new-romono/P1.bmp new file mode 100644 index 0000000..fa63701 Binary files /dev/null and b/bmps/times-new-romono/P1.bmp differ diff --git a/bmps/times-new-romono/Q0.bmp b/bmps/times-new-romono/Q0.bmp new file mode 100644 index 0000000..d4185ae Binary files /dev/null and b/bmps/times-new-romono/Q0.bmp differ diff --git a/bmps/times-new-romono/R1.bmp b/bmps/times-new-romono/R1.bmp new file mode 100644 index 0000000..2189d01 Binary files /dev/null and b/bmps/times-new-romono/R1.bmp differ diff --git a/bmps/times-new-romono/S0.bmp b/bmps/times-new-romono/S0.bmp new file mode 100644 index 0000000..e10d89d Binary files /dev/null and b/bmps/times-new-romono/S0.bmp differ diff --git a/bmps/times-new-romono/T1.bmp b/bmps/times-new-romono/T1.bmp new file mode 100644 index 0000000..9abd78b Binary files /dev/null and b/bmps/times-new-romono/T1.bmp differ diff --git a/bmps/times-new-romono/U0.bmp b/bmps/times-new-romono/U0.bmp new file mode 100644 index 0000000..f780801 Binary files /dev/null and b/bmps/times-new-romono/U0.bmp differ diff --git a/bmps/times-new-romono/V0.bmp b/bmps/times-new-romono/V0.bmp new file mode 100644 index 0000000..ce41b9d Binary files /dev/null and b/bmps/times-new-romono/V0.bmp differ diff --git a/bmps/times-new-romono/W0.bmp b/bmps/times-new-romono/W0.bmp new file mode 100644 index 0000000..7d314fc Binary files /dev/null and b/bmps/times-new-romono/W0.bmp differ diff --git a/bmps/times-new-romono/X1.bmp b/bmps/times-new-romono/X1.bmp new file mode 100644 index 0000000..505b655 Binary files /dev/null and b/bmps/times-new-romono/X1.bmp differ diff --git a/bmps/times-new-romono/Y1.bmp b/bmps/times-new-romono/Y1.bmp new file mode 100644 index 0000000..8c7f561 Binary files /dev/null and b/bmps/times-new-romono/Y1.bmp differ diff --git a/bmps/times-new-romono/Z1.bmp b/bmps/times-new-romono/Z1.bmp new file mode 100644 index 0000000..9f2ee00 Binary files /dev/null and b/bmps/times-new-romono/Z1.bmp differ diff --git a/bmps/times-new-romono/[0.bmp b/bmps/times-new-romono/[0.bmp new file mode 100644 index 0000000..a815fb1 Binary files /dev/null and b/bmps/times-new-romono/[0.bmp differ diff --git a/bmps/times-new-romono/]0.bmp b/bmps/times-new-romono/]0.bmp new file mode 100644 index 0000000..fe64dec Binary files /dev/null and b/bmps/times-new-romono/]0.bmp differ diff --git a/bmps/times-new-romono/^0.bmp b/bmps/times-new-romono/^0.bmp new file mode 100644 index 0000000..edc74e4 Binary files /dev/null and b/bmps/times-new-romono/^0.bmp differ diff --git a/bmps/times-new-romono/_9.bmp b/bmps/times-new-romono/_9.bmp new file mode 100644 index 0000000..131939f Binary files /dev/null and b/bmps/times-new-romono/_9.bmp differ diff --git a/bmps/times-new-romono/`0.bmp b/bmps/times-new-romono/`0.bmp new file mode 100644 index 0000000..dbbd47b Binary files /dev/null and b/bmps/times-new-romono/`0.bmp differ diff --git a/bmps/times-new-romono/a4.bmp b/bmps/times-new-romono/a4.bmp new file mode 100644 index 0000000..6ce75a2 Binary files /dev/null and b/bmps/times-new-romono/a4.bmp differ diff --git a/bmps/times-new-romono/b0.bmp b/bmps/times-new-romono/b0.bmp new file mode 100644 index 0000000..7aa6910 Binary files /dev/null and b/bmps/times-new-romono/b0.bmp differ diff --git a/bmps/times-new-romono/c4.bmp b/bmps/times-new-romono/c4.bmp new file mode 100644 index 0000000..4b01cb0 Binary files /dev/null and b/bmps/times-new-romono/c4.bmp differ diff --git a/bmps/times-new-romono/d0.bmp b/bmps/times-new-romono/d0.bmp new file mode 100644 index 0000000..7038199 Binary files /dev/null and b/bmps/times-new-romono/d0.bmp differ diff --git a/bmps/times-new-romono/e4.bmp b/bmps/times-new-romono/e4.bmp new file mode 100644 index 0000000..ec69414 Binary files /dev/null and b/bmps/times-new-romono/e4.bmp differ diff --git a/bmps/times-new-romono/f0.bmp b/bmps/times-new-romono/f0.bmp new file mode 100644 index 0000000..c305bf7 Binary files /dev/null and b/bmps/times-new-romono/f0.bmp differ diff --git a/bmps/times-new-romono/g4.bmp b/bmps/times-new-romono/g4.bmp new file mode 100644 index 0000000..91e8dcb Binary files /dev/null and b/bmps/times-new-romono/g4.bmp differ diff --git a/bmps/times-new-romono/h0.bmp b/bmps/times-new-romono/h0.bmp new file mode 100644 index 0000000..c34b689 Binary files /dev/null and b/bmps/times-new-romono/h0.bmp differ diff --git a/bmps/times-new-romono/i0.bmp b/bmps/times-new-romono/i0.bmp new file mode 100644 index 0000000..1060f09 Binary files /dev/null and b/bmps/times-new-romono/i0.bmp differ diff --git a/bmps/times-new-romono/j2.bmp b/bmps/times-new-romono/j2.bmp new file mode 100644 index 0000000..4ad08f8 Binary files /dev/null and b/bmps/times-new-romono/j2.bmp differ diff --git a/bmps/times-new-romono/k0.bmp b/bmps/times-new-romono/k0.bmp new file mode 100644 index 0000000..588eda3 Binary files /dev/null and b/bmps/times-new-romono/k0.bmp differ diff --git a/bmps/times-new-romono/l0.bmp b/bmps/times-new-romono/l0.bmp new file mode 100644 index 0000000..a41c814 Binary files /dev/null and b/bmps/times-new-romono/l0.bmp differ diff --git a/bmps/times-new-romono/m4.bmp b/bmps/times-new-romono/m4.bmp new file mode 100644 index 0000000..dc76566 Binary files /dev/null and b/bmps/times-new-romono/m4.bmp differ diff --git a/bmps/times-new-romono/n4.bmp b/bmps/times-new-romono/n4.bmp new file mode 100644 index 0000000..454b977 Binary files /dev/null and b/bmps/times-new-romono/n4.bmp differ diff --git a/bmps/times-new-romono/o4.bmp b/bmps/times-new-romono/o4.bmp new file mode 100644 index 0000000..4c16844 Binary files /dev/null and b/bmps/times-new-romono/o4.bmp differ diff --git a/bmps/times-new-romono/p4.bmp b/bmps/times-new-romono/p4.bmp new file mode 100644 index 0000000..caf0de1 Binary files /dev/null and b/bmps/times-new-romono/p4.bmp differ diff --git a/bmps/times-new-romono/q4.bmp b/bmps/times-new-romono/q4.bmp new file mode 100644 index 0000000..5f79f69 Binary files /dev/null and b/bmps/times-new-romono/q4.bmp differ diff --git a/bmps/times-new-romono/r4.bmp b/bmps/times-new-romono/r4.bmp new file mode 100644 index 0000000..15375e0 Binary files /dev/null and b/bmps/times-new-romono/r4.bmp differ diff --git a/bmps/times-new-romono/s4.bmp b/bmps/times-new-romono/s4.bmp new file mode 100644 index 0000000..5eccdbc Binary files /dev/null and b/bmps/times-new-romono/s4.bmp differ diff --git a/bmps/times-new-romono/t2.bmp b/bmps/times-new-romono/t2.bmp new file mode 100644 index 0000000..4db29b7 Binary files /dev/null and b/bmps/times-new-romono/t2.bmp differ diff --git a/bmps/times-new-romono/u4.bmp b/bmps/times-new-romono/u4.bmp new file mode 100644 index 0000000..5c54ad1 Binary files /dev/null and b/bmps/times-new-romono/u4.bmp differ diff --git a/bmps/times-new-romono/v4.bmp b/bmps/times-new-romono/v4.bmp new file mode 100644 index 0000000..288869c Binary files /dev/null and b/bmps/times-new-romono/v4.bmp differ diff --git a/bmps/times-new-romono/w4.bmp b/bmps/times-new-romono/w4.bmp new file mode 100644 index 0000000..a9559e4 Binary files /dev/null and b/bmps/times-new-romono/w4.bmp differ diff --git a/bmps/times-new-romono/x4.bmp b/bmps/times-new-romono/x4.bmp new file mode 100644 index 0000000..1b58d2c Binary files /dev/null and b/bmps/times-new-romono/x4.bmp differ diff --git a/bmps/times-new-romono/y4.bmp b/bmps/times-new-romono/y4.bmp new file mode 100644 index 0000000..92d018a Binary files /dev/null and b/bmps/times-new-romono/y4.bmp differ diff --git a/bmps/times-new-romono/z4.bmp b/bmps/times-new-romono/z4.bmp new file mode 100644 index 0000000..959bbbe Binary files /dev/null and b/bmps/times-new-romono/z4.bmp differ diff --git a/bmps/times-new-romono/{0.bmp b/bmps/times-new-romono/{0.bmp new file mode 100644 index 0000000..1cbd65a Binary files /dev/null and b/bmps/times-new-romono/{0.bmp differ diff --git a/bmps/times-new-romono/|0.bmp b/bmps/times-new-romono/|0.bmp new file mode 100644 index 0000000..365983d Binary files /dev/null and b/bmps/times-new-romono/|0.bmp differ diff --git a/bmps/times-new-romono/}0.bmp b/bmps/times-new-romono/}0.bmp new file mode 100644 index 0000000..71a17d0 Binary files /dev/null and b/bmps/times-new-romono/}0.bmp differ diff --git a/bmps/times-new-romono/~5.bmp b/bmps/times-new-romono/~5.bmp new file mode 100644 index 0000000..742ea57 Binary files /dev/null and b/bmps/times-new-romono/~5.bmp differ diff --git a/bmps/times-new-romono/█1.bmp b/bmps/times-new-romono/█1.bmp new file mode 100644 index 0000000..fd27b34 Binary files /dev/null and b/bmps/times-new-romono/█1.bmp differ diff --git a/bmps/times-new-romono/𐘋0.bmp b/bmps/times-new-romono/𐘋0.bmp new file mode 100644 index 0000000..4a0de14 Binary files /dev/null and b/bmps/times-new-romono/𐘋0.bmp differ diff --git a/src/components/highlight_button.rs b/src/components/highlight_button.rs index 516c8fc..fca949e 100644 --- a/src/components/highlight_button.rs +++ b/src/components/highlight_button.rs @@ -38,12 +38,12 @@ impl Component for HighlightButton { vec![ //highlight background DrawInstructions::Rect(self.top_left, self.size, theme_info.top), - DrawInstructions::Text([self.top_left[0] + 4, self.top_left[1] + (self.size[1] - font_height) / 2], "times-new-roman", self.text.to_string(), theme_info.text_top, theme_info.top, None), + DrawInstructions::Text([self.top_left[0] + 4, self.top_left[1] + (self.size[1] - font_height) / 2], "times-new-roman", self.text.to_string(), theme_info.top_text, theme_info.top, None, None), ] } else { vec![ DrawInstructions::Rect(self.top_left, self.size, theme_info.background), - DrawInstructions::Text([self.top_left[0] + 4, self.top_left[1] + (self.size[1] - font_height) / 2], "times-new-roman", self.text.to_string(), theme_info.text, theme_info.background, None), + DrawInstructions::Text([self.top_left[0] + 4, self.top_left[1] + (self.size[1] - font_height) / 2], "times-new-roman", self.text.to_string(), theme_info.text, theme_info.background, None, None), ] } } diff --git a/src/components/toggle_button.rs b/src/components/toggle_button.rs index 5368df2..c519df4 100644 --- a/src/components/toggle_button.rs +++ b/src/components/toggle_button.rs @@ -54,7 +54,7 @@ impl Component for ToggleButton { //the background if self.draw_bg //DrawInstructions::Rect(), //the text (for now, hardcoded top left) - DrawInstructions::Text([self.top_left[0] + 4, self.top_left[1] + (self.size[1] - font_height) / 2], "times-new-roman", self.text.to_string(), theme_info.text, theme_info.background, None), + DrawInstructions::Text([self.top_left[0] + 4, self.top_left[1] + (self.size[1] - font_height) / 2], "times-new-roman", self.text.to_string(), theme_info.text, theme_info.background, None, None), ] } diff --git a/src/framebuffer.rs b/src/framebuffer.rs index 871ec81..e3692ad 100644 --- a/src/framebuffer.rs +++ b/src/framebuffer.rs @@ -164,27 +164,26 @@ impl FramebufferWriter { //todo, config space for c in text.chars() { if c == ' ' { - top_left[0] += 5; + top_left[0] += mono_width.unwrap_or(5) as usize; } else { - let char_info = get_font_char(&("./bmps/".to_string() + font_name), c); - if let Some(char_info) = char_info { - let char_width = char_info.1[0].len(); - let add_after: usize; - if let Some(mono_width) = mono_width { - let mono_width = mono_width as usize; - let remainder = if mono_width < char_width { - 0 - } else { - mono_width - char_width - }; + //so a ? char must be in every font + let char_info = get_font_char(&("./bmps/".to_string() + font_name), c).unwrap_or(get_font_char(&("./bmps/".to_string() + font_name), '?').unwrap()); + let char_width = char_info.1[0].len(); + let add_after: usize; + if let Some(mono_width) = mono_width { + let mono_width = mono_width as usize; + if mono_width < char_width { + add_after = mono_width; + } else { + let remainder = mono_width - char_width; top_left[0] += remainder / 2; add_after = remainder - remainder / 2 + char_width; - } else { - add_after = char_width + horiz_spacing; - } - self.draw_char(top_left, &char_info, color, bg_color); - top_left[0] += add_after; + }; + } else { + add_after = char_width + horiz_spacing; } + self.draw_char(top_left, &char_info, color, bg_color); + top_left[0] += add_after; } } } diff --git a/src/fs.rs b/src/fs.rs index 9a577b5..d91cd93 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -4,7 +4,6 @@ use bmp_rust::bmp::BMP; pub fn get_font_char(dir: &str, c: char) -> Option<(char, Vec>, u8)> { let c = if c == '/' { '𐘋' } else { c }; - let mut font: Vec<(char, Vec>, u8)> = Vec::new(); for entry in read_dir(dir).unwrap() { let path = entry.unwrap().path(); let path_chars: Vec = path.file_name().unwrap().to_str().unwrap().to_string().chars().collect(); diff --git a/src/keyboard.rs b/src/keyboard.rs index a6cb19d..1dcedb7 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -13,6 +13,7 @@ pub fn key_to_char(key: Key) -> Option { Key::Char(c) => Some(KeyChar::Press(c)), Key::Alt(c) => Some(KeyChar::Alt(c)), Key::Backspace => Some(KeyChar::Press('𐘁')), + Key::Esc => Some(KeyChar::Press('𐘃')), _ => None, } } diff --git a/src/main.rs b/src/main.rs index 5ed51ae..74c2118 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,8 @@ mod messages; mod fs; +mod utils; + fn main() { let mut fb = Framebuffer::new("/dev/fb0").unwrap(); let bytes_per_pixel = (fb.var_screen_info.bits_per_pixel as usize) / 8; diff --git a/src/messages.rs b/src/messages.rs index 420b7cc..6db678a 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -69,6 +69,7 @@ pub enum ShortcutType { MoveWindowToEdge(Direction), CenterWindow, FullscreenWindow, + HalfWidthWindow, //half width, full height // } diff --git a/src/themes.rs b/src/themes.rs index 7ac1b99..8343fe0 100644 --- a/src/themes.rs +++ b/src/themes.rs @@ -13,9 +13,10 @@ pub struct ThemeInfo { pub border_left_top: RGBColor, pub border_right_bottom: RGBColor, pub text: RGBColor, - pub text_top: RGBColor, + pub top_text: RGBColor, pub alt_background: RGBColor, pub alt_text: RGBColor, + pub alt_secondary: RGBColor, // } @@ -26,9 +27,10 @@ const THEME_INFOS: [(Themes, ThemeInfo); 1] = [ border_left_top: [255, 255, 255], border_right_bottom: [0, 0, 0], text: [0, 0, 0], - text_top: [255, 255, 255], + top_text: [255, 255, 255], alt_background: [0, 0, 0], alt_text: [255, 255, 255], + alt_secondary: [128, 128, 128], // }), ]; diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..805afc8 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,28 @@ + +//the tuple is first, line #, actual line +pub fn calc_actual_lines<'a>(lines: impl Iterator, max_chars_per_line: usize) -> Vec<(bool, usize, String)> { + let mut actual_lines = Vec::new(); + let mut line_num = 0; + for real_line in lines { + let mut line = real_line.to_string(); + let mut first = true; + loop { + if line.chars().count() <= max_chars_per_line { + actual_lines.push((first, line_num, line)); + break; + } else { + let mut line_chars = line.chars(); + let mut push_string = String::new(); + for _i in 0..max_chars_per_line { + push_string += &line_chars.next().unwrap().to_string(); + } + actual_lines.push((first, line_num, push_string)); + line = line_chars.collect(); + } + first = false; + } + line_num += 1; + } + actual_lines +} + diff --git a/src/window_likes/lock_screen.rs b/src/window_likes/lock_screen.rs index 76fc895..b1cca50 100644 --- a/src/window_likes/lock_screen.rs +++ b/src/window_likes/lock_screen.rs @@ -50,11 +50,11 @@ impl WindowLike for LockScreen { fn draw(&self, _theme_info: &ThemeInfo) -> Vec { vec![ DrawInstructions::Rect([0, 0], self.dimensions, [0, 0, 0]), - DrawInstructions::Text([4, 4], "times-new-roman", "The bulldozer outside the kitchen window was quite a big one.".to_string(), [255, 255, 255], [0, 0, 0], None), - DrawInstructions::Text([4, 4 + 16], "times-new-roman", "\"Yellow,\" he thought, and stomped off back to his bedroom to get dressed.".to_string(), [255, 255, 255], [0, 0, 0], None), - DrawInstructions::Text([4, 4 + 16 * 2], "times-new-roman", "He stared at it.".to_string(), [255, 255, 255], [0, 0, 0], None), - DrawInstructions::Text([4, 4 + 16 * 3], "times-new-roman", "Password: ".to_string(), [255, 255, 255], [0, 0, 0], None), - DrawInstructions::Text([77, 4 + 16 * 3], "times-new-roman", "*".repeat(self.input_password.len()), [255, 255, 255], [0, 0, 0], None), + DrawInstructions::Text([4, 4], "times-new-roman", "The bulldozer outside the kitchen window was quite a big one.".to_string(), [255, 255, 255], [0, 0, 0], None, None), + DrawInstructions::Text([4, 4 + 16], "times-new-roman", "\"Yellow,\" he thought, and stomped off back to his bedroom to get dressed.".to_string(), [255, 255, 255], [0, 0, 0], None, None), + DrawInstructions::Text([4, 4 + 16 * 2], "times-new-roman", "He stared at it.".to_string(), [255, 255, 255], [0, 0, 0], None, None), + DrawInstructions::Text([4, 4 + 16 * 3], "times-new-roman", "Password: ".to_string(), [255, 255, 255], [0, 0, 0], None, None), + DrawInstructions::Text([77, 4 + 16 * 3], "times-new-roman", "*".repeat(self.input_password.len()), [255, 255, 255], [0, 0, 0], None, None), ] } diff --git a/src/window_likes/malvim.rs b/src/window_likes/malvim.rs new file mode 100644 index 0000000..36d2dc3 --- /dev/null +++ b/src/window_likes/malvim.rs @@ -0,0 +1,354 @@ +use std::vec::Vec; +use std::vec; +use std::fmt; +use std::path::PathBuf; +use std::fs::{ read_to_string, write }; + +use crate::messages::{ WindowMessage, WindowMessageResponse }; +use crate::themes::ThemeInfo; +use crate::framebuffer::Dimensions; +use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType }; +use crate::utils::calc_actual_lines; + +const MONO_WIDTH: u8 = 10; +const LINE_HEIGHT: usize = 18; +const PADDING: usize = 2; +const BAND_HEIGHT: usize = 18; + +struct FileInfo { + pub name: String, + pub path: String, + pub changed: bool, + pub top_line_pos: usize, + pub line_pos: usize, + pub cursor_pos: usize, + pub content: Vec, + // +} + +#[derive(Default, PartialEq)] +enum State { + #[default] + None, + // +} + +#[derive(Default, PartialEq)] +enum Mode { + #[default] + Normal, + Insert, + Command, +} + +impl fmt::Display for Mode { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let write_str = match self { + Mode::Normal => "NORMAL", + Mode::Insert => "INSERT", + Mode::Command => "COMMAND", + }; + fmt.write_str(write_str)?; + Ok(()) + } +} + +#[derive(Default)] +struct Current { + pub actual_lines: Vec<(bool, usize, String)>, //first, line #, actual line + pub line_num_width: usize, //file line digits * MONO_WIDTH + pub max_lines: usize, //max actual lines on screen + pub max_chars_per_line: usize, +} + +#[derive(Default)] +pub struct Malvim { + dimensions: Dimensions, + state: State, + mode: Mode, + command: Option, + bottom_message: Option, + maybe_num: Option, + files: Vec, + current_file_index: usize, + current: Current, +} + +impl WindowLike for Malvim { + fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse { + match message { + WindowMessage::Init(dimensions) => { + self.dimensions = dimensions; + WindowMessageResponse::JustRerender + }, + WindowMessage::KeyPress(key_press) => { + if key_press.key == '𐘃' { //escape key + self.mode = Mode::Normal; + self.state = State::None; + } else if key_press.key == ':' && self.mode == Mode::Normal && self.state == State::None { + self.mode = Mode::Command; + self.command = Some(String::new()); + } else if key_press.key == 'i' && self.mode == Mode::Normal && self.state == State::None && self.files.len() > 0 { + self.mode = Mode::Insert; + } else if self.mode == Mode::Insert { + let current_file = &self.files[self.current_file_index]; + if key_press.key == '𐘂' { //the enter key + // + } else if key_press.key == '𐘁' { //backspace + // + } else { + // + } + } else if self.mode == Mode::Normal && self.files.len() > 0 { + let current_file = &mut self.files[self.current_file_index]; + if key_press.key == 'h' { + current_file.cursor_pos = current_file.cursor_pos.checked_sub(1).unwrap_or(0); + } else if key_press.key == 'j' { + // + } else if key_press.key == 'k' { + // + } else if key_press.key == 'l' { + current_file.cursor_pos += 1; + if current_file.cursor_pos == current_file.content[current_file.line_pos].len() { + current_file.cursor_pos = current_file.content[current_file.line_pos].len() - 1; + } + } else if key_press.key == '0' { + current_file.cursor_pos = 0; + } else if key_press.key == '$' { + current_file.cursor_pos = current_file.content[current_file.line_pos].len().checked_sub(1).unwrap_or(0); + } + // + } else if self.mode == Mode::Command { + self.bottom_message = None; + let command = self.command.clone().unwrap_or("".to_string()); + if key_press.key == '𐘂' { //the enter key + self.process_command(); + self.command = None; + self.mode = Mode::Normal; + } else if key_press.key == '𐘁' { //backspace + if command.len() > 0 { + self.command = Some(command[..command.len() - 1].to_string()); + } + } else { + self.command = Some(command.to_string() + &key_press.key.to_string()); + } + } else { + return WindowMessageResponse::DoNothing; + } + self.calc_current(); //too over zealous but whatever + WindowMessageResponse::JustRerender + }, + WindowMessage::ChangeDimensions(dimensions) => { + self.dimensions = dimensions; + WindowMessageResponse::JustRerender + }, + _ => WindowMessageResponse::DoNothing, + } + } + + fn draw(&self, theme_info: &ThemeInfo) -> Vec { + let mut instructions = vec![ + //the top white bar + DrawInstructions::Rect([0, 0], [self.dimensions[0], BAND_HEIGHT], theme_info.alt_text), + //black background + DrawInstructions::Rect([0, BAND_HEIGHT], [self.dimensions[0], self.dimensions[1] - BAND_HEIGHT * 3], theme_info.alt_background), + //slight above bottom blue bar + DrawInstructions::Rect([0, self.dimensions[1] - BAND_HEIGHT * 2], [self.dimensions[0], BAND_HEIGHT], theme_info.top), + //black background + DrawInstructions::Rect([0, self.dimensions[1] - BAND_HEIGHT], [self.dimensions[0], BAND_HEIGHT], theme_info.alt_background), + ]; + //write file tabs + let mut used_width = 0; + for file_index in 0..self.files.len() { + let file_info = &self.files[file_index]; + let future_used_width = used_width + 4 + file_info.name.len() * MONO_WIDTH as usize; + //just cut off when too many file tabs open to fit + if future_used_width > self.dimensions[0] { + break; + } + let background = if file_index == self.current_file_index { + theme_info.alt_background + } else { + theme_info.alt_secondary + }; + instructions.extend(vec![ + DrawInstructions::Rect([used_width, 2], [file_info.name.len() * MONO_WIDTH as usize + 4, BAND_HEIGHT - 2], background), + DrawInstructions::Text([used_width + 2, 2], "times-new-romono", file_info.name.clone(), theme_info.alt_text, background, Some(0), Some(MONO_WIDTH)), + ]); + used_width = future_used_width; + } + //write the actual current file + let mut sub_line_num = 0; //a line in a file can be split into multiple lines for display + if self.files.len() > 0 { + let current_file = &self.files[self.current_file_index]; + let current = &self.current; + for line_num in current_file.top_line_pos..(current_file.top_line_pos + current.max_lines) { + if line_num == current.actual_lines.len() { + break; + } + let line = ¤t.actual_lines[line_num]; + let rel_line_num = line_num - current_file.top_line_pos; + //write line num text (if start of line) + let y0 = BAND_HEIGHT + rel_line_num * LINE_HEIGHT + PADDING; + if line.0 { + instructions.push(DrawInstructions::Text([PADDING, y0], "times-new-romono", line.1.to_string(), theme_info.alt_secondary, theme_info.alt_background, Some(0), Some(MONO_WIDTH))); + sub_line_num = 0; + } + let x1 = current.line_num_width + PADDING * 2; + //write actual line + //line.2 + instructions.push(DrawInstructions::Text([x1, y0], "times-new-romono", line.2.clone(), theme_info.alt_text, theme_info.alt_background, Some(0), Some(MONO_WIDTH))); + sub_line_num += 1; + let max = sub_line_num * current.max_chars_per_line; + let min = max - current.max_chars_per_line; + if line.1 == current_file.line_pos && current_file.cursor_pos >= min && current_file.cursor_pos < max { + let top_left = [x1 + (current_file.cursor_pos - min) * MONO_WIDTH as usize, y0]; + //the cursor is on this line, draw it + instructions.push(DrawInstructions::Rect(top_left, [MONO_WIDTH as usize, LINE_HEIGHT], theme_info.top)); + //draw the char over it + instructions.push(DrawInstructions::Text(top_left, "times-new-romono", line.2.chars().nth(current_file.cursor_pos - min).unwrap().to_string(), theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH))); + } + } + } + //bottom blue band stuff + //write mode + instructions.push(DrawInstructions::Text([0, self.dimensions[1] - BAND_HEIGHT * 2 + 1], "times-new-romono", self.mode.to_string(), theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH))); + let file_status; + if self.files.len() > 0 { + file_status = self.files[self.current_file_index].name.clone(); + } else { + file_status = "No file open".to_string(); + } + instructions.push(DrawInstructions::Text([self.dimensions[0] - file_status.len() * (MONO_WIDTH as usize), self.dimensions[1] - BAND_HEIGHT * 2 + 1], "times-new-romono", file_status, theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH))); + //write command or bottom message + if self.mode == Mode::Command { + instructions.push(DrawInstructions::Text([0, self.dimensions[1] - BAND_HEIGHT], "times-new-romono", ":".to_string() + &self.command.clone().unwrap_or("".to_string()), theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH))); + } else if self.mode == Mode::Normal && self.bottom_message.is_some() { + instructions.push(DrawInstructions::Text([0, self.dimensions[1] - BAND_HEIGHT], "times-new-romono", self.bottom_message.clone().unwrap(), theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH))); + } + instructions + } + + fn title(&self) -> &'static str { + "Malvim" + } + + fn subtype(&self) -> WindowLikeType { + WindowLikeType::Window + } + + fn ideal_dimensions(&self, _dimensions: Dimensions) -> Dimensions { + [410, 410] + } + + fn resizable(&self) -> bool { + true + } +} + +impl Malvim { + pub fn new() -> Self { + Default::default() + } + + fn calc_current(&mut self) { + if self.files.len() == 0 { + return; + } + let current_file = &self.files[self.current_file_index]; + let line_num_width = current_file.content.len().to_string().len() * MONO_WIDTH as usize; + let max_chars_per_line = (self.dimensions[0] - line_num_width - PADDING * 2) / MONO_WIDTH as usize; + let actual_lines = calc_actual_lines(current_file.content.iter(), max_chars_per_line); + //now, see if the line_pos is still visible from the top_line_pos, + //if not, move top_line_pos down until it is + let max_lines = (self.dimensions[1] - BAND_HEIGHT * 3 - PADDING) / LINE_HEIGHT; + if current_file.top_line_pos + max_lines < current_file.line_pos { + self.files[self.current_file_index].top_line_pos = current_file.line_pos.checked_sub(max_lines).unwrap_or(0); + } + self.current = Current { + actual_lines, + line_num_width, + max_lines, + max_chars_per_line, + }; + } + + fn process_command(&mut self) { + let mut parts = self.command.as_ref().unwrap().split(" "); + let first = parts.next().unwrap(); + let arg = parts.next().unwrap_or(""); + if first == "e" || first == "edit" || ((first == "t" || first == "tabe") && self.files.len() > 0) { + //find the file and open it + let mut failed = false; + let mut new_path = if self.files.len() > 0 { + PathBuf::from(self.files[self.current_file_index].path.clone()).parent().unwrap().to_path_buf() + } else { + PathBuf::from("/") + }; + for part in arg.split("/") { + if part == ".." { + if let Some(parent) = new_path.parent() { + new_path = parent.to_path_buf(); + } else { + failed = true; + } + } else { + new_path.push(part); + } + } + if !failed && new_path.is_file() { + let name = new_path.file_name().unwrap().to_string_lossy().into_owned(); + let path = new_path.to_string_lossy().into_owned(); + if let Ok(content) = read_to_string(new_path) { + let file_info = FileInfo { + name, + path, + changed: false, + top_line_pos: 0, + line_pos: 0, + cursor_pos: 0, + content: content.split("\n").map(|s| s.to_string()).collect(), + }; + if first == "e" || first == "edit" { + if self.files.len() > 0 { + self.files[self.current_file_index] = file_info; + } else { + self.files.push(file_info); + } + } else { + //t(abe) + self.current_file_index += 1; + if self.current_file_index == self.files.len() - 1 { + self.files.push(file_info); + } else { + self.files.insert(self.current_file_index, file_info); + } + } + } else { + self.bottom_message = Some("Failed to open that file".to_string()); + } + } else { + self.bottom_message = Some("That is not a file or does not exist".to_string()); + } + } else if self.files.len() == 0 { + self.bottom_message = Some("No files are open, so can only do :e(dit)".to_string()); + } else if first == "w" || first == "write" { + let current_file = &self.files[self.current_file_index]; + write(¤t_file.path, ¤t_file.content.join("\n")); + self.files[self.current_file_index].changed = false; + } else if first == "q" || first == "quit" { + self.files.remove(self.current_file_index); + self.current_file_index = self.current_file_index.checked_sub(1).unwrap_or(0); + } else if first == "p" || first == "tabp" { + self.current_file_index = self.current_file_index.checked_sub(1).unwrap_or(self.files.len() - 1); + } else if first == "n" || first == "tabn" { + self.current_file_index += 1; + if self.current_file_index == self.files.len() { + self.current_file_index = 0; + } + } else { + self.bottom_message = Some("Not a command".to_string()); + } + } +} + diff --git a/src/window_likes/minesweeper.rs b/src/window_likes/minesweeper.rs index 664f4f2..8e16048 100644 --- a/src/window_likes/minesweeper.rs +++ b/src/window_likes/minesweeper.rs @@ -3,7 +3,7 @@ use std::vec; use std::collections::VecDeque; use core::convert::TryFrom; -use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType, WINDOW_TOP_HEIGHT }; +use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType }; use crate::messages::{ WindowMessage, WindowMessageResponse }; use crate::framebuffer::Dimensions; use crate::themes::ThemeInfo; @@ -156,20 +156,20 @@ impl WindowLike for Minesweeper { fn draw(&self, theme_info: &ThemeInfo) -> Vec { if self.state == MinesweeperState::Seed { vec![ - DrawInstructions::Text([4, WINDOW_TOP_HEIGHT + 4], "times-new-roman", "Type in random characters to initalise the seed".to_string(), theme_info.text, theme_info.background, None), - DrawInstructions::Text([4, WINDOW_TOP_HEIGHT + 4 + 16], "times-new-roman", self.random_chars.clone(), theme_info.text, theme_info.background, None), + DrawInstructions::Text([4, 4], "times-new-roman", "Type in random characters to initalise the seed".to_string(), theme_info.text, theme_info.background, None, None), + DrawInstructions::Text([4, 4 + 16], "times-new-roman", self.random_chars.clone(), theme_info.text, theme_info.background, None, None), ] } else { let mut instructions = vec![ //top border - DrawInstructions::Rect([1, WINDOW_TOP_HEIGHT], [self.dimensions[0] - 7, 5], [128, 128, 128]), - DrawInstructions::Rect([self.dimensions[0] - 6, WINDOW_TOP_HEIGHT], [4, 1], [128, 128, 128]), - DrawInstructions::Rect([self.dimensions[0] - 6, WINDOW_TOP_HEIGHT + 1], [3, 1], [128, 128, 128]), - DrawInstructions::Rect([self.dimensions[0] - 6, WINDOW_TOP_HEIGHT + 2], [2, 1], [128, 128, 128]), - DrawInstructions::Rect([self.dimensions[0] - 6, WINDOW_TOP_HEIGHT + 3], [1, 1], [128, 128, 128]), - DrawInstructions::Rect([self.dimensions[0] - 6, WINDOW_TOP_HEIGHT + 4], [1, 1], [128, 128, 128]), + DrawInstructions::Rect([1, 0], [self.dimensions[0] - 7, 5], [128, 128, 128]), + DrawInstructions::Rect([self.dimensions[0] - 6, 0], [4, 1], [128, 128, 128]), + DrawInstructions::Rect([self.dimensions[0] - 6, 1], [3, 1], [128, 128, 128]), + DrawInstructions::Rect([self.dimensions[0] - 6, 2], [2, 1], [128, 128, 128]), + DrawInstructions::Rect([self.dimensions[0] - 6, 3], [1, 1], [128, 128, 128]), + DrawInstructions::Rect([self.dimensions[0] - 6, 4], [1, 1], [128, 128, 128]), //left border - DrawInstructions::Rect([1, WINDOW_TOP_HEIGHT], [5, self.dimensions[1] - WINDOW_TOP_HEIGHT - 5], [128, 128, 128]), + DrawInstructions::Rect([1, 0], [5, self.dimensions[1] - 5], [128, 128, 128]), DrawInstructions::Rect([1, self.dimensions[1] - 5], [1, 4], [128, 128, 128]), DrawInstructions::Rect([2, self.dimensions[1] - 5], [1, 3], [128, 128, 128]), DrawInstructions::Rect([3, self.dimensions[1] - 5], [1, 2], [128, 128, 128]), @@ -181,12 +181,12 @@ impl WindowLike for Minesweeper { DrawInstructions::Rect([3, self.dimensions[1] - 3], [1, 2], [255, 255, 255]), DrawInstructions::Rect([2, self.dimensions[1] - 2], [1, 1], [255, 255, 255]), //right border - DrawInstructions::Rect([self.dimensions[0] - 6, WINDOW_TOP_HEIGHT + 5], [5, self.dimensions[1] - WINDOW_TOP_HEIGHT], [255, 255, 255]), - DrawInstructions::Rect([self.dimensions[0] - 2, WINDOW_TOP_HEIGHT], [1, 5], [255, 255, 255]), - DrawInstructions::Rect([self.dimensions[0] - 3, WINDOW_TOP_HEIGHT + 1], [1, 4], [255, 255, 255]), - DrawInstructions::Rect([self.dimensions[0] - 4, WINDOW_TOP_HEIGHT + 2], [1, 3], [255, 255, 255]), - DrawInstructions::Rect([self.dimensions[0] - 5, WINDOW_TOP_HEIGHT + 3], [1, 2], [255, 255, 255]), - DrawInstructions::Rect([self.dimensions[0] - 6, WINDOW_TOP_HEIGHT + 4], [1, 1], [255, 255, 255]), + DrawInstructions::Rect([self.dimensions[0] - 6, 5], [5, self.dimensions[1]], [255, 255, 255]), + DrawInstructions::Rect([self.dimensions[0] - 2, 0], [1, 5], [255, 255, 255]), + DrawInstructions::Rect([self.dimensions[0] - 3, 1], [1, 4], [255, 255, 255]), + DrawInstructions::Rect([self.dimensions[0] - 4, 2], [1, 3], [255, 255, 255]), + DrawInstructions::Rect([self.dimensions[0] - 5, 3], [1, 2], [255, 255, 255]), + DrawInstructions::Rect([self.dimensions[0] - 6, 4], [1, 1], [255, 255, 255]), ]; let tile_size = (self.dimensions[0] - 10) / 16; for y in 0..16 { @@ -194,7 +194,7 @@ impl WindowLike for Minesweeper { let tile = &self.tiles[y][x]; if tile.revealed { if tile.mine { - instructions.push(DrawInstructions::Text([x * tile_size + tile_size / 2 + 2, WINDOW_TOP_HEIGHT + y * tile_size + tile_size / 2], "times-new-roman", "x".to_string(), [255, 0, 0], theme_info.background, None)); + instructions.push(DrawInstructions::Text([x * tile_size + tile_size / 2 + 2, y * tile_size + tile_size / 2], "times-new-roman", "x".to_string(), [255, 0, 0], theme_info.background, None, None)); } else { let color = match tile.touching { 1 => [0, 0, 255], @@ -207,10 +207,10 @@ impl WindowLike for Minesweeper { //8 _ => [128, 128, 128], }; - instructions.push(DrawInstructions::Text([x * tile_size + tile_size / 2 + 5, WINDOW_TOP_HEIGHT + y * tile_size + tile_size / 2 + 2], "times-new-roman", tile.touching.to_string(), color, theme_info.background, None)); + instructions.push(DrawInstructions::Text([x * tile_size + tile_size / 2 + 5, y * tile_size + tile_size / 2 + 2], "times-new-roman", tile.touching.to_string(), color, theme_info.background, None, None)); } } else { - let top_left = [x * tile_size + 6, WINDOW_TOP_HEIGHT + y * tile_size + 5]; + let top_left = [x * tile_size + 6, y * tile_size + 5]; //do not do the corners in respect of our poor poor heap (vector size too big would be bad) instructions.extend(vec![ //top border @@ -225,15 +225,15 @@ impl WindowLike for Minesweeper { //right bottom DrawInstructions::Rect([top_left[0] + tile_size - 4, top_left[1] + 3], [3, tile_size - 4], [128, 128, 128]), // - DrawInstructions::Text([x * tile_size + tile_size / 2 - 2, WINDOW_TOP_HEIGHT + y * tile_size + tile_size / 2], "times-new-roman", u8_to_hex((y * 16 + x) as u8), theme_info.text, theme_info.background, None), + DrawInstructions::Text([x * tile_size + tile_size / 2 - 2, y * tile_size + tile_size / 2], "times-new-roman", u8_to_hex((y * 16 + x) as u8), theme_info.text, theme_info.background, None, None), ]); } } } if self.state == MinesweeperState::Lost { - instructions.extend(vec![DrawInstructions::Text([4, WINDOW_TOP_HEIGHT + 4], "times-new-roman", "You LOST!!! Press a key to play again.".to_string(), theme_info.text, theme_info.background, None)]); + instructions.extend(vec![DrawInstructions::Text([4, 4], "times-new-roman", "You LOST!!! Press a key to play again.".to_string(), theme_info.text, theme_info.background, None, None)]); } else if self.state == MinesweeperState::Won { - instructions.extend(vec![DrawInstructions::Text([4, WINDOW_TOP_HEIGHT + 4], "times-new-roman", "You WON!!! Press a key to play again.".to_string(), theme_info.text, theme_info.background, None)]); + instructions.extend(vec![DrawInstructions::Text([4, 4], "times-new-roman", "You WON!!! Press a key to play again.".to_string(), theme_info.text, theme_info.background, None, None)]); } instructions } @@ -249,7 +249,7 @@ impl WindowLike for Minesweeper { } fn ideal_dimensions(&self, _dimensions: Dimensions) -> Dimensions { - [410, 410 + WINDOW_TOP_HEIGHT] + [410, 410] } } diff --git a/src/window_likes/mod.rs b/src/window_likes/mod.rs index 31435b7..dea554b 100644 --- a/src/window_likes/mod.rs +++ b/src/window_likes/mod.rs @@ -6,4 +6,5 @@ pub mod workspace_indicator; pub mod minesweeper; pub mod terminal; +pub mod malvim; diff --git a/src/window_likes/start_menu.rs b/src/window_likes/start_menu.rs index 17a82ed..493457d 100644 --- a/src/window_likes/start_menu.rs +++ b/src/window_likes/start_menu.rs @@ -141,6 +141,8 @@ impl StartMenu { let mut to_add: Vec<&str> = Vec::new(); if name == "Games" { to_add.push("Minesweeper"); + } else if name == "Editing" { + to_add.push("Malvim"); } else if name == "Files" { to_add.push("Terminal"); } diff --git a/src/window_likes/terminal.rs b/src/window_likes/terminal.rs index 4ac3169..df3c774 100644 --- a/src/window_likes/terminal.rs +++ b/src/window_likes/terminal.rs @@ -5,12 +5,12 @@ use std::str::from_utf8; use std::path::PathBuf; use std::io; -use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType, WINDOW_TOP_HEIGHT }; +use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType }; use crate::messages::{ WindowMessage, WindowMessageResponse }; use crate::framebuffer::Dimensions; use crate::themes::ThemeInfo; -const MONO_WIDTH: u8 = 8; +const MONO_WIDTH: u8 = 10; const LINE_HEIGHT: usize = 15; const PADDING: usize = 4; @@ -84,17 +84,16 @@ impl WindowLike for Terminal { fn draw(&self, theme_info: &ThemeInfo) -> Vec { let mut instructions = vec![ DrawInstructions::Rect([0, 0], self.dimensions, theme_info.alt_background), - // ]; //add the visible lines of text let end_line = self.actual_line_num + self.get_max_lines(); - let mut text_y = WINDOW_TOP_HEIGHT + PADDING; + let mut text_y = PADDING; for line_num in self.actual_line_num..end_line { if line_num == self.actual_lines.len() { break; } let line = self.actual_lines[line_num].clone(); - instructions.push(DrawInstructions::Text([PADDING, text_y], "times-new-roman", line, theme_info.alt_text, theme_info.alt_background, Some(MONO_WIDTH))); + instructions.push(DrawInstructions::Text([PADDING, text_y], "times-new-romono", line, theme_info.alt_text, theme_info.alt_background, Some(0), Some(MONO_WIDTH))); text_y += LINE_HEIGHT; } instructions @@ -109,7 +108,7 @@ impl WindowLike for Terminal { } fn ideal_dimensions(&self, _dimensions: Dimensions) -> Dimensions { - [410, 410 + WINDOW_TOP_HEIGHT] + [410, 410] } fn resizable(&self) -> bool { @@ -123,7 +122,7 @@ impl Terminal { } fn get_max_lines(&self) -> usize { - (self.dimensions[1] - WINDOW_TOP_HEIGHT- PADDING * 2) / LINE_HEIGHT + (self.dimensions[1] - PADDING * 2) / LINE_HEIGHT } fn process_command(&mut self) -> CommandResponse { @@ -177,13 +176,12 @@ impl Terminal { //cannot index or do .len() because those count bytes not characters loop { if working_line.chars().count() <= max_chars_per_line { - self.actual_lines.push(working_line); break; } else { let mut working_line_chars = working_line.chars(); let mut push_string = String::new(); - for i in 0..max_chars_per_line { + for _i in 0..max_chars_per_line { push_string += &working_line_chars.next().unwrap().to_string(); } self.actual_lines.push(push_string); diff --git a/src/window_likes/workspace_indicator.rs b/src/window_likes/workspace_indicator.rs index 53b7b4b..3433f35 100644 --- a/src/window_likes/workspace_indicator.rs +++ b/src/window_likes/workspace_indicator.rs @@ -44,9 +44,9 @@ impl WindowLike for WorkspaceIndicator { for w in 0..9 { if w == self.current_workspace as usize { instructions.push(DrawInstructions::Rect([w * WIDTH, 0], [WIDTH, self.dimensions[1]], theme_info.top)); - instructions.push(DrawInstructions::Text([w * WIDTH + 5, 4], "times-new-roman", (w + 1).to_string(), theme_info.text_top, theme_info.top, None)); + instructions.push(DrawInstructions::Text([w * WIDTH + 5, 4], "times-new-roman", (w + 1).to_string(), theme_info.top_text, theme_info.top, None, None)); } else { - instructions.push(DrawInstructions::Text([w * WIDTH + 5, 4], "times-new-roman", (w + 1).to_string(), theme_info.text, theme_info.background, None)); + instructions.push(DrawInstructions::Text([w * WIDTH + 5, 4], "times-new-roman", (w + 1).to_string(), theme_info.text, theme_info.background, None, None)); } } instructions diff --git a/src/window_manager.rs b/src/window_manager.rs index 5a6afee..0ec6508 100644 --- a/src/window_manager.rs +++ b/src/window_manager.rs @@ -1,6 +1,6 @@ use std::vec::Vec; use std::vec; -use std::collections::HashMap; +use std::collections::{ HashMap, VecDeque }; use std::fmt; use std::boxed::Box; use std::sync::{ LazyLock, Mutex }; @@ -24,10 +24,13 @@ use crate::messages::*; use crate::window_likes::start_menu::StartMenu; use crate::window_likes::minesweeper::Minesweeper; use crate::window_likes::terminal::Terminal; +use crate::window_likes::malvim::Malvim; + +//todo, better error handling for windows pub const TASKBAR_HEIGHT: usize = 38; pub const INDICATOR_HEIGHT: usize = 20; -pub const WINDOW_TOP_HEIGHT: usize = 26; +const WINDOW_TOP_HEIGHT: usize = 26; static WRITER: LazyLock> = LazyLock::new(|| Mutex::new(Default::default())); @@ -48,7 +51,9 @@ pub fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) { for c in stdin.keys() { if let Some(kc) = key_to_char(c.unwrap()) { + //do not allow exit when locked unless debugging if kc == KeyChar::Alt('e') { + //if kc == KeyChar::Alt('e') && !wm.locked { write!(stdout, "{}", cursor::Show).unwrap(); stdout.suspend_raw_mode().unwrap(); exit(0); @@ -68,7 +73,7 @@ pub fn min(one: usize, two: usize) -> usize { #[derive(Debug)] pub enum DrawInstructions { Rect(Point, Dimensions, RGBColor), - Text(Point, &'static str, String, RGBColor, RGBColor, Option), //font and text + Text(Point, &'static str, String, RGBColor, RGBColor, Option, Option), //font and text Gradient(Point, Dimensions, RGBColor, RGBColor, usize), Mingde(Point), } @@ -126,7 +131,7 @@ pub struct WindowManager { dimensions: Dimensions, theme: Themes, focused_id: usize, - locked: bool, + pub locked: bool, current_workspace: u8, framebuffer: Framebuffer, } @@ -156,6 +161,7 @@ impl WindowManager { let id = self.id_count; self.focused_id = id; window_like.handle_message(WindowMessage::Init(dimensions)); + let dimensions = if window_like.subtype() == WindowLikeType::Window { [dimensions[0], dimensions[1] + WINDOW_TOP_HEIGHT] } else { dimensions }; self.window_infos.push(WindowLikeInfo { id, window_like, @@ -242,11 +248,13 @@ impl WindowManager { if !self.locked { //keyboard shortcut let shortcuts = HashMap::from([ + //alt+e is terminate program (ctrl+c) ('s', ShortcutType::StartMenu), (']', ShortcutType::FocusNextWindow), ('q', ShortcutType::QuitWindow), ('c', ShortcutType::CenterWindow), ('f', ShortcutType::FullscreenWindow), + ('w', ShortcutType::HalfWidthWindow), //move window a small amount ('h', ShortcutType::MoveWindow(Direction::Left)), ('j', ShortcutType::MoveWindow(Direction::Down)), @@ -422,11 +430,14 @@ impl WindowManager { } else { new_dimensions = self.window_infos[focused_index].dimensions; } - self.window_infos[focused_index].window_like.handle_message(WindowMessage::ChangeDimensions(new_dimensions)); + self.window_infos[focused_index].window_like.handle_message(WindowMessage::ChangeDimensions([new_dimensions[0], new_dimensions[1] - WINDOW_TOP_HEIGHT])); press_response = WindowMessageResponse::JustRerender; } } - } + }, + &ShortcutType::HalfWidthWindow => { + // + }, }; } } @@ -471,6 +482,7 @@ impl WindowManager { } let w: WindowBox = match w { "Minesweeper" => Box::new(Minesweeper::new()), + "Malvim" => Box::new(Malvim::new()), "Terminal" => Box::new(Terminal::new()), "StartMenu" => Box::new(StartMenu::new()), _ => panic!("no such window"), @@ -510,6 +522,10 @@ impl WindowManager { }; } + fn get_true_top_left(top_left: &Point, is_window: bool) -> Point { + [top_left[0], top_left[1] + if is_window { WINDOW_TOP_HEIGHT } else { 0 }] + } + //another issue with a huge vector of draw instructions; it takes up heap memory pub fn render(&mut self, maybe_redraw_ids: Option>, use_saved_buffer: bool) { let theme_info = get_theme_info(&self.theme).unwrap(); @@ -540,17 +556,24 @@ impl WindowManager { } else { window_info.dimensions }; - let mut instructions = Vec::new(); - if window_info.window_like.subtype() == WindowLikeType::Window { + let mut instructions = VecDeque::from(window_info.window_like.draw(&theme_info)); + let is_window = window_info.window_like.subtype() == WindowLikeType::Window; + if is_window { //if this is the top most window to draw, snapshot if w_index == max_index && !use_saved_buffer && redraw_ids.len() == 0 { WRITER.lock().unwrap().save_buffer(); } + //offset top left by the window top height for windows (because windows can't draw in that region) + instructions = instructions.iter().map(|instruction| { + match instruction { + DrawInstructions::Rect(top_left, dimensions, color) => DrawInstructions::Rect(WindowManager::get_true_top_left(top_left, is_window), *dimensions, *color), + DrawInstructions::Text(top_left, font_name, text, color, bg_color, horiz_spacing, mono_width) => DrawInstructions::Text(WindowManager::get_true_top_left(top_left, is_window), font_name, text.clone(), *color, *bg_color, *horiz_spacing, *mono_width), + DrawInstructions::Mingde(top_left) => DrawInstructions::Mingde(WindowManager::get_true_top_left(top_left, is_window)), + DrawInstructions::Gradient(top_left, dimensions, start_color, end_color, steps) => DrawInstructions::Gradient(WindowManager::get_true_top_left(top_left, is_window), *dimensions, *start_color, *end_color, *steps), + } + }).collect(); //draw window background - instructions.push(DrawInstructions::Rect([0, 0], window_dimensions, theme_info.background)); - } - instructions.extend(window_info.window_like.draw(&theme_info)); - if window_info.window_like.subtype() == WindowLikeType::Window { + instructions.push_front(DrawInstructions::Rect([0, 0], window_dimensions, theme_info.background)); //draw window top decorations and what not instructions.extend(vec![ //left top border @@ -558,7 +581,7 @@ impl WindowManager { DrawInstructions::Rect([0, 0], [1, window_dimensions[1]], theme_info.border_left_top), //top DrawInstructions::Rect([1, 1], [window_dimensions[0] - 2, WINDOW_TOP_HEIGHT - 3], theme_info.top), - DrawInstructions::Text([4, 4], "times-new-roman", window_info.window_like.title().to_string(), theme_info.text_top, theme_info.top, None), + DrawInstructions::Text([4, 4], "times-new-roman", window_info.window_like.title().to_string(), theme_info.top_text, theme_info.top, None, None), //top bottom border DrawInstructions::Rect([1, WINDOW_TOP_HEIGHT - 2], [window_dimensions[0] - 2, 2], theme_info.border_left_top), //right bottom border @@ -587,8 +610,8 @@ impl WindowManager { ]; window_writer.draw_rect(top_left, true_dimensions, color); }, - DrawInstructions::Text(top_left, font_name, text, color, bg_color, mono_width) => { - window_writer.draw_text(top_left, font_name, &text, color, bg_color, 1, mono_width); + DrawInstructions::Text(top_left, font_name, text, color, bg_color, horiz_spacing, mono_width) => { + window_writer.draw_text(top_left, font_name, &text, color, bg_color, horiz_spacing.unwrap_or(1), mono_width); }, DrawInstructions::Mingde(top_left) => { window_writer._draw_mingde(top_left);