Compare commits
368 Commits
Author | SHA1 | Date |
---|---|---|
|
86aa5fb001 | |
|
262426fb44 | |
|
ec0807ce2b | |
|
ce1d9457f9 | |
|
664ce4dfae | |
|
7b6af30194 | |
|
e0c55e73d2 | |
|
4efbfa7a1d | |
|
9669a63c5d | |
|
bf250b5ac6 | |
|
aaebb4c5d9 | |
|
36514cbb68 | |
|
1552eedc18 | |
|
4523eda0fa | |
|
3acf473792 | |
|
6c3149c0fe | |
|
784eac740f | |
|
a88937c508 | |
|
96eac58cf5 | |
|
b8781b1b64 | |
|
126b9a30a2 | |
|
994449b1c2 | |
|
5ebb13a454 | |
|
6127fa1cbb | |
|
54bbe72aa8 | |
|
b373eab346 | |
|
e73cfaf490 | |
|
046e6b4d6c | |
|
efb5ede4b9 | |
|
5e6d7cffdf | |
|
76e15fa1df | |
|
75014f4b28 | |
|
9ef16c1bf2 | |
|
da54c0f039 | |
|
6d1dfed3db | |
|
65cf744a86 | |
|
caf7428ba3 | |
|
948d2af071 | |
|
ddd7709182 | |
|
9912c3eb82 | |
|
660d9e7e3a | |
|
0124d25c34 | |
|
ed3789c23f | |
|
6f739933b7 | |
|
4b79e862a7 | |
|
dc329ed18c | |
|
3e7287baea | |
|
04406b3561 | |
|
0638183356 | |
|
f24c509859 | |
|
a000c06987 | |
|
3b9e5beff6 | |
|
ad34230d67 | |
|
57b6efd831 | |
|
f36f9cfd0f | |
|
a5800917e8 | |
|
c64592f326 | |
|
377b08eeb6 | |
|
22e139e2e5 | |
|
6b7fafbde2 | |
|
3f09b2b6e4 | |
|
31324ad083 | |
|
ad299911f1 | |
|
3d1bb9680a | |
|
9cd1d582e2 | |
|
3ca1fff755 | |
|
5a9f3565aa | |
|
1f33979af9 | |
|
1e79d31a02 | |
|
08a5b198c7 | |
|
c215e3c2c4 | |
|
c5472a42a2 | |
|
fa9ec36dbb | |
|
8c459da9f7 | |
|
552bdeb1e6 | |
|
0a60272440 | |
|
a575a08bcc | |
|
57e5ff22dc | |
|
733c595c54 | |
|
535646335a | |
|
8d24366492 | |
|
6df064282c | |
|
6ea6ffaa3d | |
|
7d793c68db | |
|
b07d8bd8f7 | |
|
42758e1459 | |
|
f30489219b | |
|
bb2732b621 | |
|
00d1256eba | |
|
cf073e9aae | |
|
90a9f84f10 | |
|
c5cada3dc9 | |
|
87d31a3d94 | |
|
c5d9522c0b | |
|
4a009f79a2 | |
|
70452bdaac | |
|
36745e0492 | |
|
9540c01d83 | |
|
58eff2a9e2 | |
|
c00084bf3f | |
|
9a08b37be4 | |
|
955afcb1a9 | |
|
af3fec6f20 | |
|
5bac0308e5 | |
|
8d8fc22745 | |
|
eba5ea8cc0 | |
|
b8b8a409ea | |
|
010bd2a33f | |
|
ba629f1892 | |
|
bff0943dc5 | |
|
2c07da350a | |
|
82ed687460 | |
|
5a84456c7a | |
|
f8b7c603a1 | |
|
419f4aef6a | |
|
12d0e185cc | |
|
e80827d890 | |
|
42eafb87c7 | |
|
ea544e429e | |
|
58cb7f3d2d | |
|
53eb14ccbd | |
|
23dfd96a87 | |
|
5827b9ace2 | |
|
c4f9bac85e | |
|
90083d5bcb | |
|
77b4bcb94b | |
|
665d2289e2 | |
|
ed024aac70 | |
|
6045a205fd | |
|
227419c1ad | |
|
7b06b81ed3 | |
|
9d1a045a1f | |
|
1923f8df96 | |
|
b730ee9459 | |
|
a6c1864f43 | |
|
1010db44a6 | |
|
078a23e0e0 | |
|
44264df09d | |
|
0aa82f5ba5 | |
|
83b4d7142a | |
|
fefa99920b | |
|
8185d8bc1a | |
|
481fa11211 | |
|
a295aa420b | |
|
d8353f9b1a | |
|
b6b5df6a18 | |
|
13a194dca5 | |
|
6c522a4b27 | |
|
dd5232fa6b | |
|
6ac82df949 | |
|
0b67b198bd | |
|
7813fc126d | |
|
fb3c16707d | |
|
5668e6f081 | |
|
e96cb4375c | |
|
c2f8cac4a9 | |
|
7e47ca9843 | |
|
37723c2b9a | |
|
16886945ed | |
|
3d104d5b4a | |
|
0b265d2b72 | |
|
5d1a3b2578 | |
|
e39bcafb78 | |
|
176831d0f3 | |
|
2a5734892a | |
|
f5d465901d | |
|
e134ecb6a4 | |
|
5bae8b7e41 | |
|
93e87a05c7 | |
|
bc15e6c879 | |
|
1918dcbbde | |
|
5675307497 | |
|
88f33afbb7 | |
|
9fa974f6c4 | |
|
c4095ef7aa | |
|
c136ca2b4c | |
|
87141b8d99 | |
|
6d991fef63 | |
|
9c7b139e5a | |
|
d3b25092f8 | |
|
96cb52f3ef | |
|
9c548faeda | |
|
d2c3db7b58 | |
|
d0e35bb98f | |
|
8522d4671e | |
|
88e9594b93 | |
|
3faacd6c43 | |
|
31c130045d | |
|
70a742c98a | |
|
546a4e022b | |
|
efebe00640 | |
|
07f6b7842e | |
|
83e9787e75 | |
|
85eda6aeb8 | |
|
8dd0f88e7c | |
|
c328a755c6 | |
|
3fa508f786 | |
|
d75bfa639c | |
|
f1eefc9a06 | |
|
6a0fd773f4 | |
|
ec68f448af | |
|
ae339cc587 | |
|
38a08d66fc | |
|
fb8d1d0940 | |
|
12f3d5ac5c | |
|
a81e931b9a | |
|
6237bf89c3 | |
|
8ce9ca882a | |
|
bf8cbddf1a | |
|
4e5c6f2515 | |
|
3cf873439d | |
|
652cf2b73b | |
|
1867bae62c | |
|
012273d8d3 | |
|
9d5cd2c865 | |
|
acfb80a2e7 | |
|
a10582c846 | |
|
5a28806bdf | |
|
d2c681f573 | |
|
acb0e43945 | |
|
65622d01cd | |
|
d61e03b877 | |
|
f0d403e834 | |
|
e627e4e822 | |
|
c91733e10d | |
|
6370d87fc2 | |
|
c5e6c66338 | |
|
a5b3a934d2 | |
|
517d844b3c | |
|
4a8fb6ab22 | |
|
ac9c6617e3 | |
|
d5d0262013 | |
|
3125bbb258 | |
|
a069e6c984 | |
|
d6f2fd983e | |
|
e3c874cd69 | |
|
eccec3ef1a | |
|
666d5ca52d | |
|
0355f7c2fb | |
|
f79cf89170 | |
|
5a2097d080 | |
|
9a3f9d0991 | |
|
2062060dac | |
|
7557486b13 | |
|
4dad234532 | |
|
3d5ee3f89b | |
|
2e0465e44a | |
|
49f8e1c043 | |
|
4bc9fa41b7 | |
|
96fdfd1338 | |
|
8d007ec10d | |
|
fdca4d2601 | |
|
417b3e5225 | |
|
e4f0524bdf | |
|
ebb03139bb | |
|
4695ada409 | |
|
a78cd82dcd | |
|
a8a918655a | |
|
39e851c76c | |
|
bac14a675b | |
|
b7cd6d07fc | |
|
f76960c907 | |
|
5b39848b64 | |
|
bbdf7fefbd | |
|
73de9899a8 | |
|
c348940ca1 | |
|
8a4afa760d | |
|
4aef276b64 | |
|
1d32c5da2b | |
|
9f38585892 | |
|
1a1881aa4e | |
|
75a5c7bf72 | |
|
11465e4043 | |
|
57fbbc62ed | |
|
ce11f999f9 | |
|
a16b53c289 | |
|
b5fe1eb8ff | |
|
85f3a08238 | |
|
439c6e3b01 | |
|
19370790f8 | |
|
bab555aa4c | |
|
7e2d7f72ef | |
|
39eb2e5210 | |
|
bb3802857b | |
|
c02399708e | |
|
aea18d5b65 | |
|
820f27c0a2 | |
|
15666309ee | |
|
aae13f2b57 | |
|
2461df0153 | |
|
368c25107a | |
|
2ace17294c | |
|
6d3dded68b | |
|
32619e7dad | |
|
2ea283ea74 | |
|
052bbb4fa6 | |
|
0104e411ae | |
|
76fad65a6e | |
|
d29c56e22d | |
|
e8b090efe6 | |
|
7b611d3dda | |
|
9dd8a8afc9 | |
|
71bbdd8487 | |
|
6b8ff221a8 | |
|
6454e540d0 | |
|
75e7070077 | |
|
e317478e6a | |
|
cf72809e26 | |
|
825be13ee9 | |
|
56cebf6db6 | |
|
94b41aa18a | |
|
a15aea03ea | |
|
a907a4418a | |
|
07ddc4e44d | |
|
063c40accd | |
|
2ee37e5c20 | |
|
8f9e47c117 | |
|
145bad6c9a | |
|
fd56d89cc8 | |
|
29d6cadbb3 | |
|
ee0f84fd8e | |
|
4800fe6620 | |
|
d4c2f8cb1b | |
|
d6f1a45c77 | |
|
dddef229dc | |
|
13b0daed7c | |
|
f2f43a2b0b | |
|
663704e3ec | |
|
f7c484328b | |
|
732f3c60e9 | |
|
6fb1a5802a | |
|
11ebc77505 | |
|
e4339f33ac | |
|
99bb171bd9 | |
|
3f2f3de751 | |
|
f1b86c4e38 | |
|
21836a8001 | |
|
00a0184918 | |
|
ab1c90e93e | |
|
4f19c8b64d | |
|
50fe92a1b1 | |
|
55735daeec | |
|
cbc579e93a | |
|
d58eee556c | |
|
467b10d3f8 | |
|
5ef1bd1021 | |
|
d97ce2aecc | |
|
a48b116a37 | |
|
9aa1dd1a37 | |
|
4fa1dd75b0 | |
|
1d7b9613a6 | |
|
3029f04bd2 | |
|
63b76b2912 | |
|
2e2e68ce07 | |
|
dab898f0f9 | |
|
44ee09f798 | |
|
eca2f2aa48 | |
|
d5be773817 | |
|
70f5c88640 | |
|
b302e33186 | |
|
e678a5cc62 | |
|
1f3d664dbd | |
|
4fbbd2cee1 | |
|
a9913b70ed | |
|
c111349531 | |
|
cabe6f0379 | |
|
50116934e3 | |
|
c3c93fc942 |
|
@ -0,0 +1,8 @@
|
|||
*.swp
|
||||
*.so
|
||||
*.pb.go
|
||||
*.patch
|
||||
go.mod
|
||||
go.sum
|
||||
gocui
|
||||
resources/*.so
|
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
49
Makefile
49
Makefile
|
@ -1,11 +1,37 @@
|
|||
all: plugin
|
||||
ldd ../gocui.so
|
||||
VERSION = $(shell git describe --tags)
|
||||
BUILDTIME = $(shell date +%Y.%m.%d)
|
||||
|
||||
plugin:
|
||||
GO111MODULE=off go build -v -buildmode=plugin -o ../gocui.so
|
||||
all: clean goimports vet gocui
|
||||
@ldd gocui.so
|
||||
|
||||
goget:
|
||||
go get -v -t -u
|
||||
vet:
|
||||
@GO111MODULE=off go vet
|
||||
@echo this go plugin builds okay
|
||||
|
||||
gocui:
|
||||
GO111MODULE=off go build -v -x -buildmode=plugin -o gocui.so \
|
||||
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
||||
|
||||
install:
|
||||
go build -buildmode=plugin -o ~/go/lib/gocui-${VERSION}.so \
|
||||
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
||||
cd ~/go/lib && ln -f -s gocui-${VERSION}.so gocui.so
|
||||
|
||||
# for testing custom golang
|
||||
custom:
|
||||
# GO111MODULE=off go build -v
|
||||
GO111MODULE=off go build -v -work -buildmode=blah
|
||||
|
||||
clean:
|
||||
rm -f gocui *.so go.*
|
||||
rm -f *.pb.go *.patch
|
||||
go-mod-clean purge
|
||||
|
||||
# Test the README.md & doc.go file
|
||||
# this runs pkgsite, the binary that does dev.go.dev
|
||||
# go install golang.org/x/pkgsite/cmd/pkgsite@latest
|
||||
pkgsite:
|
||||
pkgsite
|
||||
|
||||
objdump:
|
||||
objdump -t ../gocui.so |less
|
||||
|
@ -14,13 +40,14 @@ log:
|
|||
reset
|
||||
tail -f /tmp/witgui.* /tmp/guilogfile
|
||||
|
||||
cleanbuild:
|
||||
go build -v -x -buildmode=plugin -o ../nocui.so
|
||||
|
||||
check-git-clean:
|
||||
@git diff-index --quiet HEAD -- || (echo "Git repository is dirty, please commit your changes first"; exit 1)
|
||||
goimports:
|
||||
goimports -w *.go
|
||||
|
||||
redomod:
|
||||
rm -f go.*
|
||||
GO111MODULE= go mod init
|
||||
GO111MODULE= go mod tidy
|
||||
|
||||
proto:
|
||||
autogenpb --proto gocuiView.proto
|
||||
make goimports
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# a console toolkit around gocui
|
||||
|
||||
* to build, run 'make build'
|
||||
* TODO: make a way to trigger plugin builds. 'package plugin' maybe?
|
||||
|
||||
[terminals that support ture color](https://github.com/termstandard/colors#truecolor-support-in-output-devices)
|
||||
[more info about color](https://jvns.ca/blog/2024/10/01/terminal-colours/)
|
80
add.go
80
add.go
|
@ -1,80 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
log "go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
var fakeStartWidth int = me.FakeW
|
||||
var fakeStartHeight int = me.TabH + me.FramePadH
|
||||
|
||||
// setup fake labels for non-visible things off screen
|
||||
func (n *node) setFake() {
|
||||
w := n.tk
|
||||
w.isFake = true
|
||||
|
||||
n.gocuiSetWH(fakeStartWidth, fakeStartHeight)
|
||||
|
||||
fakeStartHeight += w.gocuiSize.Height()
|
||||
// TODO: use the actual max hight of the terminal window
|
||||
if fakeStartHeight > 24 {
|
||||
fakeStartHeight = me.TabH
|
||||
fakeStartWidth += me.FakeW
|
||||
}
|
||||
if true {
|
||||
n.showView()
|
||||
}
|
||||
}
|
||||
|
||||
// set the widget start width & height
|
||||
func (n *node) addWidget() {
|
||||
nw := n.tk
|
||||
log.Log(INFO, "setStartWH() w.id =", n.WidgetId, "n.name", n.progname)
|
||||
switch n.WidgetType {
|
||||
case widget.Root:
|
||||
log.Log(INFO, "setStartWH() rootNode w.id =", n.WidgetId, "w.name", n.progname)
|
||||
nw.color = &colorRoot
|
||||
n.setFake()
|
||||
return
|
||||
case widget.Flag:
|
||||
nw.color = &colorFlag
|
||||
n.setFake()
|
||||
return
|
||||
case widget.Window:
|
||||
nw.frame = false
|
||||
nw.color = &colorWindow
|
||||
// redoWindows(0,0)
|
||||
return
|
||||
case widget.Tab:
|
||||
nw.color = &colorTab
|
||||
// redoWindows(0,0)
|
||||
return
|
||||
case widget.Button:
|
||||
nw.color = &colorButton
|
||||
case widget.Box:
|
||||
nw.color = &colorBox
|
||||
nw.isFake = true
|
||||
n.setFake()
|
||||
return
|
||||
case widget.Grid:
|
||||
nw.color = &colorGrid
|
||||
nw.isFake = true
|
||||
n.setFake()
|
||||
return
|
||||
case widget.Group:
|
||||
nw.color = &colorGroup
|
||||
nw.frame = false
|
||||
return
|
||||
case widget.Label:
|
||||
nw.color = &colorLabel
|
||||
nw.frame = false
|
||||
return
|
||||
default:
|
||||
/*
|
||||
if n.IsCurrent() {
|
||||
n.updateCurrent()
|
||||
}
|
||||
*/
|
||||
}
|
||||
n.showWidgetPlacement(true, "addWidget()")
|
||||
}
|
47
checkbox.go
47
checkbox.go
|
@ -1,33 +1,40 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
// "github.com/awesome-gocui/gocui"
|
||||
log "go.wit.com/log"
|
||||
"go.wit.com/toolkits/tree"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func (n *node) setCheckbox(b any) {
|
||||
w := n.tk
|
||||
// this comes from the application
|
||||
func setChecked(n *tree.Node, b bool) {
|
||||
if n.WidgetType != widget.Checkbox {
|
||||
}
|
||||
|
||||
n.State.Checked = b
|
||||
var tk *guiWidget
|
||||
tk = n.TK.(*guiWidget)
|
||||
|
||||
tk.setCheckbox()
|
||||
}
|
||||
|
||||
// redraw the checkbox
|
||||
func (tk *guiWidget) setCheckbox() {
|
||||
if tk.WidgetType() != widget.Checkbox {
|
||||
log.Log(WARN, "setCheckbox() being run on widget:", tk.WidgetType())
|
||||
return
|
||||
}
|
||||
if widget.GetBool(b) {
|
||||
n.value = b
|
||||
n.tk.label = "X " + n.label
|
||||
if tk.Checked() {
|
||||
log.Log(WARN, "setCheckbox() got true", tk.Checked())
|
||||
tk.labelN = "X " + tk.GetLabel()
|
||||
} else {
|
||||
n.value = b
|
||||
n.tk.label = " " + n.label
|
||||
log.Log(WARN, "setCheckbox() got false", tk.Checked())
|
||||
tk.labelN = "_ " + tk.GetLabel()
|
||||
}
|
||||
t := len(n.tk.label) + 1
|
||||
w.gocuiSize.w1 = w.gocuiSize.w0 + t
|
||||
|
||||
// w.realWidth = w.gocuiSize.Width() + me.PadW
|
||||
// w.realHeight = w.gocuiSize.Height() + me.PadH
|
||||
|
||||
// if w.frame {
|
||||
// w.realWidth += me.FramePadW
|
||||
// w.realHeight += me.FramePadH
|
||||
// }
|
||||
|
||||
n.deleteView()
|
||||
n.showView()
|
||||
tk.Hide()
|
||||
tk.Show()
|
||||
}
|
||||
|
|
357
click.go
357
click.go
|
@ -1,357 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
// set isCurrent = false everywhere
|
||||
func unsetCurrent(n *node) {
|
||||
w := n.tk
|
||||
w.isCurrent = false
|
||||
|
||||
if n.WidgetType == widget.Tab {
|
||||
// n.tk.color = &colorTab
|
||||
// n.setColor()
|
||||
}
|
||||
|
||||
for _, child := range n.children {
|
||||
unsetCurrent(child)
|
||||
}
|
||||
}
|
||||
|
||||
// when adding a new widget, this will update the display
|
||||
// of the current widgets if that widget is supposed
|
||||
// to be in current display
|
||||
func (n *node) updateCurrent() {
|
||||
log.Log(NOW, "updateCurrent()", n.progname)
|
||||
if n.WidgetType == widget.Tab {
|
||||
if n.IsCurrent() {
|
||||
// n.tk.color = &colorActiveT
|
||||
n.setColor(&colorActiveT)
|
||||
n.hideView()
|
||||
n.showView()
|
||||
setCurrentTab(n)
|
||||
} else {
|
||||
// n.tk.color = &colorTab
|
||||
// n.setColor()
|
||||
}
|
||||
return
|
||||
}
|
||||
if n.WidgetType == widget.Window {
|
||||
if n.IsCurrent() {
|
||||
// setCurrentWindow(n)
|
||||
}
|
||||
return
|
||||
}
|
||||
if n.WidgetType == widget.Root {
|
||||
return
|
||||
}
|
||||
n.parent.updateCurrent()
|
||||
}
|
||||
|
||||
// shows the widgets in a window
|
||||
func setCurrentWindow(n *node) {
|
||||
if n.IsCurrent() {
|
||||
return
|
||||
}
|
||||
w := n.tk
|
||||
if n.WidgetType != widget.Window {
|
||||
return
|
||||
}
|
||||
unsetCurrent(me.rootNode)
|
||||
|
||||
if n.hasTabs {
|
||||
// set isCurrent = true on the first tab
|
||||
for _, child := range n.children {
|
||||
child.tk.isCurrent = true
|
||||
break
|
||||
}
|
||||
} else {
|
||||
w.isCurrent = true
|
||||
}
|
||||
}
|
||||
|
||||
// shows the widgets in a tab
|
||||
func setCurrentTab(n *node) {
|
||||
w := n.tk
|
||||
if n.WidgetType != widget.Tab {
|
||||
return
|
||||
}
|
||||
unsetCurrent(me.rootNode)
|
||||
w.isCurrent = true
|
||||
p := n.parent.tk
|
||||
p.isCurrent = true
|
||||
log.Log(NOW, "setCurrent()", n.progname)
|
||||
}
|
||||
|
||||
func (n *node) doWidgetClick() {
|
||||
switch n.WidgetType {
|
||||
case widget.Root:
|
||||
// THIS IS THE BEGINING OF THE LAYOUT
|
||||
log.Log(NOW, "doWidgetClick()", n.progname)
|
||||
redoWindows(0, 0)
|
||||
case widget.Flag:
|
||||
log.Log(NOW, "doWidgetClick() FLAG widget name =", n.progname)
|
||||
log.Log(NOW, "doWidgetClick() if this is the dropdown menu, handle it here?")
|
||||
case widget.Window:
|
||||
if me.currentWindow == n {
|
||||
return
|
||||
}
|
||||
if me.currentWindow != nil {
|
||||
unsetCurrent(me.currentWindow)
|
||||
me.currentWindow.setColor(&colorWindow)
|
||||
me.currentWindow.hideWidgets()
|
||||
}
|
||||
n.hideWidgets()
|
||||
me.currentWindow = n
|
||||
// setCurrentWindow(n) // probably delete this
|
||||
n.setColor(&colorActiveW)
|
||||
n.redoTabs(me.TabW, me.TabH)
|
||||
for _, child := range n.children {
|
||||
if child.currentTab == true {
|
||||
log.Log(NOW, "FOUND CURRENT TAB", child.progname)
|
||||
setCurrentTab(child)
|
||||
child.placeWidgets(me.RawW, me.RawH)
|
||||
child.showWidgets()
|
||||
return
|
||||
}
|
||||
}
|
||||
/* FIXME: redo this
|
||||
if ! n.hasTabs {
|
||||
}
|
||||
*/
|
||||
case widget.Tab:
|
||||
if n.IsCurrent() {
|
||||
return // do nothing if you reclick on the already selected tab
|
||||
}
|
||||
// find the window and disable the active tab
|
||||
p := n.parent
|
||||
if p != nil {
|
||||
p.hideWidgets()
|
||||
p.redoTabs(me.TabW, me.TabH)
|
||||
unsetCurrent(p)
|
||||
for _, child := range p.children {
|
||||
if child.WidgetType == widget.Tab {
|
||||
child.setColor(&colorTab)
|
||||
n.currentTab = false
|
||||
}
|
||||
}
|
||||
}
|
||||
n.currentTab = true
|
||||
n.setColor(&colorActiveT)
|
||||
setCurrentTab(n)
|
||||
n.placeWidgets(me.RawW, me.RawH)
|
||||
n.showWidgets()
|
||||
case widget.Group:
|
||||
// n.placeWidgets(p.tk.startH, newH)
|
||||
n.toggleTree()
|
||||
case widget.Checkbox:
|
||||
if widget.GetBool(n.value) {
|
||||
n.setCheckbox(false)
|
||||
} else {
|
||||
n.setCheckbox(true)
|
||||
}
|
||||
n.doUserEvent()
|
||||
case widget.Grid:
|
||||
newR := n.realGocuiSize()
|
||||
|
||||
// w,h := n.logicalSize()
|
||||
// w := newR.w1 - newR.w0
|
||||
// h := newR.h1 - newR.h0
|
||||
|
||||
n.placeGrid(newR.w0, newR.h0)
|
||||
n.showWidgets()
|
||||
case widget.Box:
|
||||
// w.showWidgetPlacement(logNow, "drawTree()")
|
||||
if n.direction == widget.Horizontal {
|
||||
log.Log(NOW, "BOX IS HORIZONTAL", n.progname)
|
||||
} else {
|
||||
log.Log(NOW, "BOX IS VERTICAL", n.progname)
|
||||
}
|
||||
// n.placeWidgets()
|
||||
n.toggleTree()
|
||||
case widget.Button:
|
||||
n.doUserEvent()
|
||||
case widget.Dropdown:
|
||||
log.Log(NOW, "do the dropdown here")
|
||||
if me.ddview == nil {
|
||||
me.ddview = addDropdown()
|
||||
tk := me.ddview.tk
|
||||
tk.gocuiSize.w0 = 20
|
||||
tk.gocuiSize.w1 = 40
|
||||
tk.gocuiSize.h0 = 10
|
||||
tk.gocuiSize.h1 = 25
|
||||
tk.v, _ = me.baseGui.SetView("ddview",
|
||||
tk.gocuiSize.w0,
|
||||
tk.gocuiSize.h0,
|
||||
tk.gocuiSize.w1,
|
||||
tk.gocuiSize.h1, 0)
|
||||
if tk.v == nil {
|
||||
return
|
||||
}
|
||||
tk.v.Wrap = true
|
||||
tk.v.Frame = true
|
||||
tk.v.Clear()
|
||||
fmt.Fprint(tk.v, "example.com\nwit.com")
|
||||
me.ddview.SetVisible(true)
|
||||
return
|
||||
}
|
||||
log.Log(NOW, "doWidgetClick() visible =", me.ddview.Visible())
|
||||
if me.ddview.Visible() {
|
||||
me.ddview.SetVisible(false)
|
||||
me.baseGui.DeleteView("ddview")
|
||||
me.ddview.tk.v = nil
|
||||
} else {
|
||||
var dnsList string
|
||||
for i, s := range n.vals {
|
||||
log.Log(NOW, "AddText()", n.progname, i, s)
|
||||
dnsList += s + "\n"
|
||||
}
|
||||
me.ddNode = n
|
||||
log.Log(NOW, "new dns list should be set to:", dnsList)
|
||||
me.ddview.label = dnsList
|
||||
me.ddview.SetText(dnsList)
|
||||
me.ddview.SetVisible(true)
|
||||
}
|
||||
for i, s := range n.vals {
|
||||
log.Log(NOW, "AddText()", n.progname, i, s)
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
var toggle bool = true
|
||||
|
||||
func (n *node) toggleTree() {
|
||||
if toggle {
|
||||
n.drawTree(toggle)
|
||||
toggle = false
|
||||
} else {
|
||||
n.hideWidgets()
|
||||
toggle = true
|
||||
}
|
||||
}
|
||||
|
||||
// display the widgets in the binary tree
|
||||
func (n *node) drawTree(draw bool) {
|
||||
w := n.tk
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
n.showWidgetPlacement(true, "drawTree()")
|
||||
if draw {
|
||||
// w.textResize()
|
||||
n.showView()
|
||||
} else {
|
||||
n.deleteView()
|
||||
}
|
||||
|
||||
for _, child := range n.children {
|
||||
child.drawTree(draw)
|
||||
}
|
||||
}
|
||||
|
||||
func click(g *gocui.Gui, v *gocui.View) error {
|
||||
// var l string
|
||||
// var err error
|
||||
|
||||
log.Log(INFO, "click() START", v.Name())
|
||||
// n := me.rootNode.findWidgetName(v.Name())
|
||||
n := findUnderMouse()
|
||||
if n != nil {
|
||||
log.Log(NOW, "click() Found widget =", n.WidgetId, n.progname, ",", n.label)
|
||||
if n.progname == "DropBox" {
|
||||
log.Log(NOW, "click() this is the dropdown menu. set a flag here what did I click? where is the mouse?")
|
||||
log.Log(NOW, "click() set a global dropdown clicked flag=true here")
|
||||
me.ddClicked = true
|
||||
}
|
||||
n.doWidgetClick()
|
||||
} else {
|
||||
log.Log(NOW, "click() could not find node name =", v.Name())
|
||||
}
|
||||
|
||||
if _, err := g.SetCurrentView(v.Name()); err != nil {
|
||||
log.Log(NOW, "click() END err =", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Log(NOW, "click() END")
|
||||
return nil
|
||||
}
|
||||
|
||||
func findUnderMouse() *node {
|
||||
var found *node
|
||||
var widgets []*node
|
||||
var f func(n *node)
|
||||
w, h := me.baseGui.MousePosition()
|
||||
|
||||
// find buttons that are below where the mouse button click
|
||||
f = func(n *node) {
|
||||
widget := n.tk
|
||||
// ignore widgets that are not visible
|
||||
if n.Visible() {
|
||||
if (widget.gocuiSize.w0 <= w) && (w <= widget.gocuiSize.w1) &&
|
||||
(widget.gocuiSize.h0 <= h) && (h <= widget.gocuiSize.h1) {
|
||||
widgets = append(widgets, n)
|
||||
found = n
|
||||
}
|
||||
}
|
||||
if n == me.ddview {
|
||||
log.Log(NOW, "findUnderMouse() found ddview")
|
||||
if n.Visible() {
|
||||
log.Log(NOW, "findUnderMouse() and ddview is visable. hide it here. TODO: find highlighted row")
|
||||
found = n
|
||||
// find the actual value here and set the dropdown widget
|
||||
me.baseGui.DeleteView("ddview")
|
||||
} else {
|
||||
log.Log(NOW, "findUnderMouse() I was lying, actually it's not found")
|
||||
}
|
||||
}
|
||||
|
||||
for _, child := range n.children {
|
||||
f(child)
|
||||
}
|
||||
}
|
||||
f(me.rootNode)
|
||||
// widgets has everything that matches
|
||||
// TODO: pop up menu with a list of them
|
||||
for _, n := range widgets {
|
||||
//log(logNow, "ctrlDown() FOUND widget", widget.id, widget.name)
|
||||
n.showWidgetPlacement(true, "findUnderMouse() FOUND")
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
// find the widget under the mouse click
|
||||
func ctrlDown(g *gocui.Gui, v *gocui.View) error {
|
||||
var found *node
|
||||
// var widgets []*node
|
||||
// var f func (n *node)
|
||||
found = findUnderMouse()
|
||||
if me.ctrlDown == nil {
|
||||
setupCtrlDownWidget()
|
||||
me.ctrlDown.label = found.progname
|
||||
me.ctrlDown.tk.cuiName = "ctrlDown"
|
||||
// me.ctrlDown.parent = me.rootNode
|
||||
}
|
||||
cd := me.ctrlDown.tk
|
||||
if found == nil {
|
||||
found = me.rootNode
|
||||
}
|
||||
me.ctrlDown.label = found.progname
|
||||
newR := found.realGocuiSize()
|
||||
cd.gocuiSize.w0 = newR.w0
|
||||
cd.gocuiSize.h0 = newR.h0
|
||||
cd.gocuiSize.w1 = newR.w1
|
||||
cd.gocuiSize.h1 = newR.h1
|
||||
if me.ctrlDown.Visible() {
|
||||
me.ctrlDown.hideView()
|
||||
} else {
|
||||
me.ctrlDown.showView()
|
||||
}
|
||||
me.ctrlDown.showWidgetPlacement(true, "ctrlDown:")
|
||||
return nil
|
||||
}
|
438
color.go
438
color.go
|
@ -1,26 +1,360 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"math/rand"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
//w.v.SelBgColor = gocui.ColorCyan
|
||||
//color.go: w.v.SelFgColor = gocui.ColorBlack
|
||||
//color.go: w.v.BgColor = gocui.ColorGreen
|
||||
// simple colors for light and dark
|
||||
|
||||
type colorT struct {
|
||||
frame gocui.Attribute
|
||||
fg gocui.Attribute
|
||||
bg gocui.Attribute
|
||||
selFg gocui.Attribute
|
||||
selBg gocui.Attribute
|
||||
name string
|
||||
// information about how terminfo works
|
||||
// https://jvns.ca/blog/2024/10/01/terminal-colours/
|
||||
|
||||
// TODO: move all this to a protobuf
|
||||
// TODO: add black/white only flag for ttyS0
|
||||
// TODO: fix kvm/qemu serial console & SIGWINCH.
|
||||
// TODO: check minicom (doesn't work)
|
||||
// TODO: fix riscv boards
|
||||
|
||||
// DONE ON ENABLE() WIDGET
|
||||
// restores the last saved color and makes it active
|
||||
func (tk *guiWidget) restoreEnableColor() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
tk.color.frame = tk.colorLast.frame
|
||||
tk.color.fg = tk.colorLast.fg
|
||||
tk.color.bg = tk.colorLast.bg
|
||||
tk.color.selFg = tk.colorLast.selFg
|
||||
tk.color.selBg = tk.colorLast.selBg
|
||||
|
||||
tk.activateColor()
|
||||
}
|
||||
|
||||
// DONE ON DISABLE() WIDGET
|
||||
// makes the button look disabled
|
||||
func (tk *guiWidget) setColorDisable() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
// save the current color
|
||||
tk.color.frame = superLightGrey
|
||||
tk.color.fg = gocui.ColorBlack
|
||||
tk.color.bg = superLightGrey
|
||||
tk.color.selFg = superLightGrey
|
||||
tk.color.selBg = superLightGrey
|
||||
tk.activateColor()
|
||||
}
|
||||
|
||||
// sets the current gocui highlight colors
|
||||
func (tk *guiWidget) activateColor() {
|
||||
if tk.v == nil {
|
||||
return
|
||||
}
|
||||
tk.v.FrameColor = tk.color.frame
|
||||
tk.v.FgColor = tk.color.fg
|
||||
tk.v.BgColor = tk.color.bg
|
||||
tk.v.SelFgColor = tk.color.selFg
|
||||
tk.v.SelBgColor = tk.color.selBg
|
||||
}
|
||||
|
||||
// saves the color and makes it active
|
||||
func (tk *guiWidget) updateColor() {
|
||||
if tk.v == nil {
|
||||
return
|
||||
}
|
||||
if tk.color != nil {
|
||||
tk.colorLast.frame = tk.color.frame
|
||||
tk.colorLast.fg = tk.color.fg
|
||||
tk.colorLast.bg = tk.color.bg
|
||||
tk.colorLast.selFg = tk.color.selFg
|
||||
tk.colorLast.selBg = tk.color.selBg
|
||||
}
|
||||
tk.activateColor()
|
||||
}
|
||||
|
||||
// Below are all the colors. TODO: move to protobuf and save in a config file
|
||||
|
||||
func (tk *guiWidget) setColorWindowFrame() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
if me.dark { // use a dark color palette
|
||||
tk.color.frame = gocui.AttrNone
|
||||
tk.color.fg = gocui.ColorBlack
|
||||
tk.color.bg = gocui.ColorBlack
|
||||
tk.color.selFg = gocui.AttrNone
|
||||
tk.color.selBg = gocui.AttrNone
|
||||
} else {
|
||||
tk.color.frame = gocui.AttrNone
|
||||
tk.color.fg = gocui.AttrNone
|
||||
tk.color.bg = gocui.AttrNone
|
||||
tk.color.selFg = gocui.AttrNone
|
||||
tk.color.selBg = gocui.AttrNone
|
||||
}
|
||||
|
||||
tk.updateColor()
|
||||
}
|
||||
|
||||
// weird. lots of color problems for me on debian sid using the traditional Andy Herzfield 'gnome'
|
||||
func (tk *guiWidget) setColorWindowTitleActive() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
if me.dark { // use a dark color palette
|
||||
tk.color.frame = gocui.AttrNone
|
||||
tk.color.fg = gocui.ColorBlue
|
||||
tk.color.bg = gocui.AttrNone
|
||||
tk.color.selFg = gocui.ColorWhite
|
||||
tk.color.selBg = gocui.ColorBlue
|
||||
} else {
|
||||
tk.color.frame = gocui.ColorWhite
|
||||
tk.color.fg = gocui.ColorWhite
|
||||
tk.color.bg = gocui.ColorBlue
|
||||
tk.color.selFg = gocui.ColorWhite
|
||||
tk.color.selBg = gocui.ColorBlue
|
||||
}
|
||||
|
||||
tk.updateColor()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) setColorWindowTitle() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
if me.dark { // use a dark color palette
|
||||
tk.color.frame = gocui.AttrNone
|
||||
tk.color.fg = gocui.ColorBlue
|
||||
tk.color.bg = gocui.AttrNone
|
||||
tk.color.selFg = gocui.ColorWhite
|
||||
tk.color.selBg = gocui.ColorBlue
|
||||
} else {
|
||||
tk.color.frame = gocui.ColorWhite
|
||||
tk.color.fg = gocui.ColorBlue
|
||||
tk.color.bg = gocui.AttrNone
|
||||
tk.color.selFg = gocui.ColorWhite
|
||||
tk.color.selBg = gocui.ColorBlue
|
||||
}
|
||||
|
||||
tk.updateColor()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) setColorBG() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
if me.dark {
|
||||
tk.color.frame = gocui.AttrNone
|
||||
tk.color.fg = gocui.ColorBlack
|
||||
tk.color.bg = gocui.ColorBlack
|
||||
tk.color.selFg = gocui.AttrNone
|
||||
tk.color.selBg = gocui.AttrNone
|
||||
} else {
|
||||
tk.color.frame = gocui.ColorWhite
|
||||
tk.color.fg = gocui.ColorWhite
|
||||
tk.color.bg = gocui.AttrNone
|
||||
tk.color.selFg = gocui.AttrNone
|
||||
tk.color.selBg = gocui.AttrNone
|
||||
}
|
||||
|
||||
tk.updateColor()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) setColorLabel() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
if me.dark {
|
||||
tk.color.frame = gocui.AttrNone
|
||||
tk.color.fg = gocui.ColorWhite
|
||||
tk.color.bg = gocui.ColorBlack
|
||||
tk.color.selFg = gocui.ColorWhite
|
||||
tk.color.selBg = gocui.AttrNone
|
||||
} else {
|
||||
tk.color.frame = gocui.AttrNone
|
||||
tk.color.fg = gocui.ColorBlack
|
||||
tk.color.bg = gocui.AttrNone
|
||||
tk.color.selFg = gocui.AttrNone
|
||||
tk.color.selBg = gocui.ColorWhite
|
||||
}
|
||||
|
||||
tk.updateColor()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) setColorLabelTable() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
if me.dark {
|
||||
tk.color.frame = gocui.AttrNone
|
||||
tk.color.fg = gocui.ColorWhite
|
||||
tk.color.bg = gocui.ColorBlack
|
||||
tk.color.selFg = gocui.ColorWhite
|
||||
tk.color.selBg = gocui.AttrNone
|
||||
} else {
|
||||
tk.color.frame = gocui.AttrNone
|
||||
tk.color.fg = gocui.ColorBlack
|
||||
tk.color.bg = gocui.AttrNone
|
||||
tk.color.selFg = gocui.AttrNone
|
||||
tk.color.selBg = gocui.ColorGreen
|
||||
}
|
||||
|
||||
tk.updateColor()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) setColorButtonDense() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
if me.dark {
|
||||
tk.color.frame = gocui.AttrNone
|
||||
tk.color.fg = gocui.ColorBlue
|
||||
tk.color.bg = gocui.ColorBlack
|
||||
tk.color.selFg = gocui.ColorWhite
|
||||
tk.color.selBg = gocui.ColorBlue
|
||||
} else {
|
||||
tk.color.frame = gocui.AttrNone
|
||||
tk.color.fg = gocui.ColorWhite
|
||||
tk.color.bg = gocui.ColorBlue
|
||||
tk.color.selFg = gocui.ColorBlue
|
||||
tk.color.selBg = gocui.AttrNone
|
||||
}
|
||||
|
||||
tk.updateColor()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) setColorNotifyIcon() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
tk.color.frame = gocui.AttrNone
|
||||
tk.color.fg = gocui.ColorWhite
|
||||
tk.color.bg = gocui.ColorBlue
|
||||
tk.color.selFg = gocui.ColorBlue
|
||||
tk.color.selBg = gocui.AttrNone
|
||||
|
||||
tk.updateColor()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) setColorButton() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
if me.dark {
|
||||
tk.color.frame = gocui.ColorBlack
|
||||
tk.color.fg = gocui.ColorBlue
|
||||
tk.color.bg = gocui.ColorBlack
|
||||
tk.color.selFg = gocui.ColorWhite
|
||||
tk.color.selBg = gocui.ColorBlue
|
||||
} else {
|
||||
tk.color.frame = gocui.ColorBlue
|
||||
tk.color.fg = gocui.AttrNone
|
||||
tk.color.bg = gocui.AttrNone
|
||||
tk.color.selFg = gocui.ColorWhite
|
||||
tk.color.selBg = gocui.ColorBlue
|
||||
}
|
||||
|
||||
tk.updateColor()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) setColorInput() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
if me.dark {
|
||||
tk.color.frame = gocui.ColorYellow
|
||||
tk.color.fg = gocui.AttrNone
|
||||
tk.color.bg = gocui.AttrNone
|
||||
tk.color.selFg = gocui.ColorYellow
|
||||
tk.color.selBg = gocui.ColorBlack
|
||||
} else {
|
||||
tk.color.frame = gocui.ColorYellow
|
||||
tk.color.fg = gocui.AttrNone
|
||||
tk.color.bg = gocui.AttrNone
|
||||
tk.color.selFg = gocui.ColorYellow
|
||||
tk.color.selBg = gocui.ColorBlack
|
||||
}
|
||||
tk.updateColor()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) setColorModal() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
if me.dark {
|
||||
tk.color.frame = gocui.ColorRed
|
||||
tk.color.fg = gocui.ColorRed
|
||||
tk.color.bg = gocui.ColorBlack
|
||||
tk.color.selFg = gocui.ColorBlack
|
||||
tk.color.selBg = gocui.AttrNone
|
||||
} else {
|
||||
tk.color.frame = gocui.ColorRed
|
||||
tk.color.fg = gocui.AttrNone
|
||||
tk.color.bg = gocui.AttrNone
|
||||
tk.color.selFg = gocui.AttrNone
|
||||
tk.color.selBg = gocui.ColorWhite
|
||||
}
|
||||
tk.updateColor()
|
||||
}
|
||||
|
||||
// what genius figured this out?
|
||||
func (tk *guiWidget) setColorTextbox() {
|
||||
if tk.color == nil {
|
||||
tk.color = new(colorT)
|
||||
}
|
||||
if me.dark {
|
||||
tk.color.frame = gocui.ColorRed
|
||||
tk.color.fg = gocui.ColorRed
|
||||
tk.color.bg = gocui.ColorBlack
|
||||
tk.color.selFg = gocui.ColorBlack
|
||||
tk.color.selBg = gocui.AttrNone
|
||||
} else {
|
||||
tk.color.frame = gocui.ColorRed
|
||||
tk.color.fg = gocui.AttrNone
|
||||
tk.color.bg = gocui.AttrNone
|
||||
tk.color.selFg = gocui.AttrNone
|
||||
tk.color.selBg = gocui.ColorWhite
|
||||
}
|
||||
tk.updateColor()
|
||||
}
|
||||
|
||||
// just notes down here
|
||||
|
||||
// what genius figured this out?
|
||||
// originally from github.com/dimasma0305/GoFetch
|
||||
func get_teminal_color_palette() string {
|
||||
// var runes rune
|
||||
// color1 := "\x1b[0;29m \x1b[0m"
|
||||
// runes = []rune(color1)
|
||||
// view.WriteRunes(runes)
|
||||
|
||||
color1 := "\x1b[0;29m \x1b[0m"
|
||||
color2 := "\x1b[0;31m \x1b[0m"
|
||||
color3 := "\x1b[0;32m \x1b[0m"
|
||||
color4 := "\x1b[0;33m \x1b[0m"
|
||||
color5 := "\x1b[0;34m \x1b[0m"
|
||||
color6 := "\x1b[0;35m \x1b[0m"
|
||||
color7 := "\x1b[0;36m \x1b[0m"
|
||||
color8 := "\x1b[0;37m \x1b[0m"
|
||||
|
||||
return color1 + " " + color2 + " " + color3 + " " + color4 + " " + color5 + " " + color6 + " " + color7 + " " + color8
|
||||
}
|
||||
|
||||
func randColor() gocui.Attribute {
|
||||
colors := []string{"Green", "#FFAA55", "Yellow", "Blue", "Red", "Black", "White"}
|
||||
i := rand.Intn(len(colors))
|
||||
log.Log(NOW, "randColor() i =", i)
|
||||
return gocui.GetColor(colors[i])
|
||||
}
|
||||
|
||||
var none gocui.Attribute = gocui.AttrNone
|
||||
var colorNone colorT = colorT{none, none, none, none, none, "debug none"}
|
||||
|
||||
var lightPurple gocui.Attribute = gocui.GetColor("#DDDDDD") // light purple
|
||||
var darkPurple gocui.Attribute = gocui.GetColor("#FFAA55") // Dark Purple
|
||||
var heavyPurple gocui.Attribute = gocui.GetColor("#88AA55") // heavy purple
|
||||
|
@ -36,85 +370,3 @@ var superLightGrey gocui.Attribute = gocui.GetColor("#55AAFF") // super light gr
|
|||
|
||||
// v.BgColor = gocui.GetColor("#55AAFF") // super light grey
|
||||
// v.BgColor = gocui.GetColor("#FFC0CB") // 'w3c pink' yellow
|
||||
|
||||
// Normal Text On mouseover
|
||||
//
|
||||
// Widget Frame Text background Text background
|
||||
var colorWindow colorT = colorT{none, gocui.ColorBlue, none, none, powdererBlue, "normal window"}
|
||||
var colorActiveW colorT = colorT{none, none, powdererBlue, none, powdererBlue, "active window"}
|
||||
|
||||
var colorTab colorT = colorT{gocui.ColorBlue, gocui.ColorBlue, none, none, powdererBlue, "normal tab"}
|
||||
var colorActiveT colorT = colorT{gocui.ColorBlue, none, powdererBlue, none, powdererBlue, "active tab"}
|
||||
|
||||
var colorButton colorT = colorT{gocui.ColorGreen, none, gocui.ColorWhite, gocui.ColorGreen, gocui.ColorBlack, "normal button"}
|
||||
var colorLabel colorT = colorT{none, none, superLightGrey, none, superLightGrey, "normal label"}
|
||||
var colorGroup colorT = colorT{none, none, superLightGrey, none, superLightGrey, "normal group"}
|
||||
|
||||
// widget debugging colors. these widgets aren't displayed unless you are debugging
|
||||
var colorRoot colorT = colorT{gocui.ColorRed, none, powdererBlue, none, gocui.ColorBlue, "debug root"}
|
||||
var colorFlag colorT = colorT{gocui.ColorRed, none, powdererBlue, none, gocui.ColorGreen, "debug flag"}
|
||||
var colorBox colorT = colorT{gocui.ColorRed, none, lightPurple, none, gocui.ColorCyan, "debug box"}
|
||||
var colorGrid colorT = colorT{gocui.ColorRed, none, lightPurple, none, gocui.ColorRed, "debug grid"}
|
||||
var colorNone colorT = colorT{none, none, none, none, none, "debug none"}
|
||||
|
||||
// actually sets the colors for the gocui element
|
||||
// the user will see the colors change when this runs
|
||||
// TODO: add black/white only flag for ttyS0
|
||||
// TODO: or fix kvm/qemu serial console & SIGWINCH.
|
||||
// TODO: and minicom and uboot and 5 million other things.
|
||||
// TODO: maybe enough of us could actually do that if we made it a goal.
|
||||
// TODO: start with riscv boards and fix it universally there
|
||||
// TODO: so just a small little 'todo' item here
|
||||
func (n *node) setColor(newColor *colorT) {
|
||||
tk := n.tk
|
||||
if tk.color == newColor {
|
||||
// nothing to do since the colors have nto changed
|
||||
return
|
||||
}
|
||||
tk.color = newColor
|
||||
if tk.v == nil {
|
||||
return
|
||||
}
|
||||
if tk.color == nil {
|
||||
log.Log(NOW, "Set the node to color = nil")
|
||||
tk.color = &colorNone
|
||||
}
|
||||
log.Log(NOW, "Set the node to color =", tk.color.name)
|
||||
n.recreateView()
|
||||
}
|
||||
|
||||
func (n *node) setDefaultWidgetColor() {
|
||||
n.showView()
|
||||
}
|
||||
|
||||
func (n *node) setDefaultHighlight() {
|
||||
w := n.tk
|
||||
if w.v == nil {
|
||||
log.Log(ERROR, "SetColor() failed on view == nil")
|
||||
return
|
||||
}
|
||||
w.v.SelBgColor = gocui.ColorGreen
|
||||
w.v.SelFgColor = gocui.ColorBlack
|
||||
}
|
||||
|
||||
func randColor() gocui.Attribute {
|
||||
colors := []string{"Green", "#FFAA55", "Yellow", "Blue", "Red", "Black", "White"}
|
||||
i := rand.Intn(len(colors))
|
||||
log.Log(NOW, "randColor() i =", i)
|
||||
return gocui.GetColor(colors[i])
|
||||
}
|
||||
|
||||
func (n *node) redoColor(draw bool) {
|
||||
w := n.tk
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Sleep(.05)
|
||||
n.setDefaultHighlight()
|
||||
n.setDefaultWidgetColor()
|
||||
|
||||
for _, child := range n.children {
|
||||
child.redoColor(draw)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
def color(num, text):
|
||||
return f"\033[38;5;{num}m{text}\033[0m"
|
||||
|
||||
for i in range(300):
|
||||
print(color(i, f"number {i:02}"))
|
218
common.go
218
common.go
|
@ -1,218 +0,0 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
These code should be common to all gui plugins
|
||||
|
||||
There are some helper functions that are probably going to be
|
||||
the same everywhere. Mostly due to handling the binary tree structure
|
||||
and the channel communication
|
||||
|
||||
For now, it's just a symlink to the 'master' version in
|
||||
./toolkit/nocui/common.go
|
||||
*/
|
||||
|
||||
import (
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
// this is the channel we send user events like
|
||||
// mouse clicks or keyboard events back to the program
|
||||
var callback chan widget.Action
|
||||
|
||||
// this is the channel we get requests to make widgets
|
||||
var pluginChan chan widget.Action
|
||||
|
||||
type node struct {
|
||||
parent *node
|
||||
children []*node
|
||||
|
||||
WidgetId int // widget ID
|
||||
WidgetType widget.WidgetType
|
||||
ParentId int // parent ID
|
||||
|
||||
state widget.State
|
||||
|
||||
// a reference name for programming and debuggign. Must be unique
|
||||
progname string
|
||||
|
||||
// the text used for button labesl, window titles, checkbox names, etc
|
||||
label string
|
||||
|
||||
// horizontal means layout widgets like books on a bookshelf
|
||||
// vertical means layout widgets like books in a stack
|
||||
// direction widget.Orientation
|
||||
direction widget.Orientation
|
||||
|
||||
// This is how the values are passed back and forth
|
||||
// values from things like checkboxes & dropdown's
|
||||
value any
|
||||
|
||||
strings []string
|
||||
|
||||
// This is used for things like a slider(0,100)
|
||||
X int
|
||||
Y int
|
||||
|
||||
// This is for the grid size & widget position
|
||||
W int
|
||||
H int
|
||||
AtW int
|
||||
AtH int
|
||||
|
||||
vals []string // dropdown menu items
|
||||
|
||||
// horizontal bool `default:false`
|
||||
|
||||
hasTabs bool // does the window have tabs?
|
||||
currentTab bool // the visible tab
|
||||
|
||||
// the internal plugin toolkit structure
|
||||
// in the gtk plugin, it has gtk things like margin & border settings
|
||||
// in the text console one, it has text console things like colors for menus & buttons
|
||||
tk *guiWidget
|
||||
}
|
||||
|
||||
// searches the binary tree for a WidgetId
|
||||
func (n *node) findWidgetId(id int) *node {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if n.WidgetId == id {
|
||||
return n
|
||||
}
|
||||
|
||||
for _, child := range n.children {
|
||||
newN := child.findWidgetId(id)
|
||||
if newN != nil {
|
||||
return newN
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) doUserEvent() {
|
||||
if callback == nil {
|
||||
log.Log(ERROR, "doUserEvent() callback == nil", n.WidgetId)
|
||||
return
|
||||
}
|
||||
var a widget.Action
|
||||
a.WidgetId = n.WidgetId
|
||||
a.Value = n.value
|
||||
a.ActionType = widget.User
|
||||
log.Log(INFO, "doUserEvent() START: send a user event to the callback channel")
|
||||
callback <- a
|
||||
log.Log(INFO, "doUserEvent() END: sent a user event to the callback channel")
|
||||
return
|
||||
}
|
||||
|
||||
// Other goroutines must use this to access the GUI
|
||||
//
|
||||
// You can not acess / process the GUI thread directly from
|
||||
// other goroutines. This is due to the nature of how
|
||||
// Linux, MacOS and Windows work (they all work differently. suprise. surprise.)
|
||||
//
|
||||
// this sets the channel to send user events back from the plugin
|
||||
func Callback(guiCallback chan widget.Action) {
|
||||
callback = guiCallback
|
||||
}
|
||||
|
||||
func PluginChannel() chan widget.Action {
|
||||
return pluginChan
|
||||
}
|
||||
|
||||
/*
|
||||
func convertString(val any) string {
|
||||
switch v := val.(type) {
|
||||
case bool:
|
||||
n.B = val.(bool)
|
||||
case string:
|
||||
n.label = val.(string)
|
||||
n.S = val.(string)
|
||||
case int:
|
||||
n.I = val.(int)
|
||||
default:
|
||||
log.Error(errors.New("Set() unknown type"), "v =", v)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// this is in common.go, do not move it
|
||||
func getString(A any) string {
|
||||
if A == nil {
|
||||
log.Warn("getString() got nil")
|
||||
return ""
|
||||
}
|
||||
var k reflect.Kind
|
||||
k = reflect.TypeOf(A).Kind()
|
||||
|
||||
switch k {
|
||||
case reflect.Int:
|
||||
var i int
|
||||
i = A.(int)
|
||||
return string(i)
|
||||
case reflect.String:
|
||||
return A.(string)
|
||||
case reflect.Bool:
|
||||
if A.(bool) == true {
|
||||
return "true"
|
||||
} else {
|
||||
return "false"
|
||||
}
|
||||
default:
|
||||
log.Warn("getString uknown kind", k, "value =", A)
|
||||
return ""
|
||||
}
|
||||
return ""
|
||||
}
|
||||
*/
|
||||
|
||||
// this is in common.go, do not move it
|
||||
func addNode(a *widget.Action) *node {
|
||||
n := new(node)
|
||||
n.WidgetType = a.WidgetType
|
||||
n.WidgetId = a.WidgetId
|
||||
n.ParentId = a.ParentId
|
||||
|
||||
n.state = a.State
|
||||
|
||||
// copy the data from the action message
|
||||
n.progname = a.ProgName
|
||||
n.value = a.Value
|
||||
n.direction = a.Direction
|
||||
n.strings = a.Strings
|
||||
|
||||
// TODO: these need to be rethought
|
||||
n.X = a.X
|
||||
n.Y = a.Y
|
||||
n.W = a.W
|
||||
n.H = a.H
|
||||
n.AtW = a.AtW
|
||||
n.AtH = a.AtH
|
||||
|
||||
// store the internal toolkit information
|
||||
n.tk = initWidget(n)
|
||||
// n.tk = new(guiWidget)
|
||||
|
||||
if a.WidgetType == widget.Root {
|
||||
log.Log(INFO, "addNode() Root")
|
||||
return n
|
||||
}
|
||||
|
||||
if me.rootNode.findWidgetId(a.WidgetId) != nil {
|
||||
log.Log(ERROR, "addNode() WidgetId already exists", a.WidgetId)
|
||||
return me.rootNode.findWidgetId(a.WidgetId)
|
||||
}
|
||||
|
||||
// add this new widget on the binary tree
|
||||
n.parent = me.rootNode.findWidgetId(a.ParentId)
|
||||
if n.parent != nil {
|
||||
n.parent.children = append(n.parent.children, n)
|
||||
//w := n.tk
|
||||
//w.parent = n.parent.tk
|
||||
//w.parent.children = append(w.parent.children, w)
|
||||
}
|
||||
return n
|
||||
}
|
116
debug.go
116
debug.go
|
@ -1,74 +1,106 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func (n *node) dumpTree(draw bool) {
|
||||
w := n.tk
|
||||
func (w *guiWidget) dumpTree(s string) {
|
||||
// log.Log(ERROR, "dump w", w.WidgetId(), w.WidgetType, w.String())
|
||||
if w == nil {
|
||||
log.Log(ERROR, "dump w.TK == nil", w.WidgetId(), w.WidgetType(), w.String())
|
||||
return
|
||||
}
|
||||
n.showWidgetPlacement(true, "dumpTree()")
|
||||
w.dumpWidget("dumpTree() " + s)
|
||||
|
||||
for _, child := range n.children {
|
||||
child.dumpTree(draw)
|
||||
for _, child := range w.children {
|
||||
child.dumpTree(s)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) showWidgetPlacement(b bool, s string) {
|
||||
if n == nil {
|
||||
log.Log(ERROR, "WTF w == nil")
|
||||
func (w *guiWidget) dumpWindows(s string) {
|
||||
// log.Log(ERROR, "dump w", w.WidgetId(), w.WidgetType, w.String())
|
||||
if w == nil {
|
||||
log.Log(ERROR, "dump w.TK == nil", w.WidgetId(), w.WidgetType(), w.String())
|
||||
return
|
||||
}
|
||||
w := n.tk
|
||||
if w.WidgetType() == widget.Window {
|
||||
s += fmt.Sprintf(" F(%d,%d)", w.force.w0, w.force.h0)
|
||||
// can't set this here. doesn't work
|
||||
// w.full.w0 = w.force.w0
|
||||
// w.full.h0 = w.force.h0
|
||||
w.dumpWidget("dumpWindow() " + s)
|
||||
w.windowFrame.dumpWidget("dumpFrame() " + s)
|
||||
}
|
||||
|
||||
for _, child := range w.children {
|
||||
child.dumpWindows(s)
|
||||
}
|
||||
}
|
||||
|
||||
// a standard function to print out information about a widget
|
||||
func (tk *guiWidget) dumpWidget(s string) {
|
||||
var s1 string
|
||||
var pId int
|
||||
if n.parent == nil {
|
||||
log.Log(INFO, "showWidgetPlacement() parent == nil", n.WidgetId, w.cuiName)
|
||||
// tk.verifyRect()
|
||||
if tk.parent == nil {
|
||||
log.Logf(WARN, "showWidgetPlacement() parent == nil wId=%d cuiName=%s", tk.WidgetId(), tk.cuiName)
|
||||
pId = 0
|
||||
} else {
|
||||
pId = n.parent.WidgetId
|
||||
pId = tk.parent.WidgetId()
|
||||
}
|
||||
s1 = fmt.Sprintf("(wId,pId)=(%2d,%2d) ", n.WidgetId, pId)
|
||||
if n.Visible() {
|
||||
s1 += fmt.Sprintf("gocui=(%2d,%2d)(%2d,%2d,%2d,%2d)",
|
||||
w.gocuiSize.Width(), w.gocuiSize.Height(),
|
||||
w.gocuiSize.w0, w.gocuiSize.h0, w.gocuiSize.w1, w.gocuiSize.h1)
|
||||
s1 = fmt.Sprintf("(wId,pId)=(%4d,%4d) ", tk.WidgetId(), pId)
|
||||
sizeW, sizeH := tk.Size()
|
||||
hide := "S"
|
||||
if tk.Hidden() {
|
||||
hide = "H"
|
||||
}
|
||||
s1 += fmt.Sprintf("size=(%3d,%3d)%s)", sizeW, sizeH, hide)
|
||||
if tk.Visible() {
|
||||
s1 += fmt.Sprintf("gocui=(%3d,%3d,%3d,%3d)",
|
||||
tk.gocuiSize.w0, tk.gocuiSize.h0, tk.gocuiSize.w1, tk.gocuiSize.h1)
|
||||
} else {
|
||||
s1 += fmt.Sprintf(" ")
|
||||
s1 += fmt.Sprintf(" %3s %3s %3s %3s ", "", "", "", "")
|
||||
}
|
||||
if n.parent != nil {
|
||||
if n.parent.WidgetType == widget.Grid {
|
||||
s1 += fmt.Sprintf("At(%2d,%2d) ", n.AtW, n.AtH)
|
||||
s1 += fmt.Sprintf(" full=(%3d,%3d,%3d,%3d)", tk.full.w0, tk.full.h0, tk.full.w1, tk.full.h1)
|
||||
if tk.parent != nil {
|
||||
if tk.parent.WidgetType() == widget.Grid {
|
||||
s1 += fmt.Sprintf("At(%3d,%3d)", tk.GridW(), tk.GridH())
|
||||
s1 += fmt.Sprintf("(%3d,%3d) ", tk.parent.widths[tk.GridW()], tk.parent.heights[tk.GridH()])
|
||||
} else {
|
||||
s1 += fmt.Sprintf(" %3s %3s ", "", "")
|
||||
s1 += fmt.Sprintf(" %3s %3s ", "", "")
|
||||
}
|
||||
} else {
|
||||
s1 += fmt.Sprintf(" %3s %3s ", "", "")
|
||||
}
|
||||
tmp := "." + n.progname + "."
|
||||
log.Log(INFO, s1, s, n.WidgetType, ",", tmp) // , "text=", w.text)
|
||||
var end string
|
||||
if tk.WidgetType() == widget.Box {
|
||||
end = fmt.Sprintf("%-8s %-8s %s %s", tk.WidgetType(), tk.cuiName, tk.Direction().String(), tk.String())
|
||||
} else {
|
||||
end = fmt.Sprintf("%-8s %-8s %s", tk.WidgetType(), tk.cuiName, tk.String())
|
||||
}
|
||||
if tk.node.InTable() {
|
||||
// log.Log(GOCUI, "findParentTAble()", tk.labelN, tk.cuiName, tk.node.WidgetId)
|
||||
end += " (table)"
|
||||
}
|
||||
log.Log(GOCUI, s1, s, end)
|
||||
}
|
||||
|
||||
func (n *node) dumpWidget(pad string) {
|
||||
log.Log(NOW, "node:", pad, n.WidgetId, "At(", n.AtW, n.AtH, ") ,", n.WidgetType, ", n.progname =", n.progname, ", n.label =", n.label)
|
||||
func printWidgetTree(g *gocui.Gui, v *gocui.View) error {
|
||||
me.treeRoot.ListWidgets()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) listWidgets() {
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var pad string
|
||||
for i := 0; i < me.depth; i++ {
|
||||
pad = pad + " "
|
||||
}
|
||||
n.dumpWidget(pad)
|
||||
|
||||
for _, child := range n.children {
|
||||
me.depth += 1
|
||||
child.listWidgets()
|
||||
me.depth -= 1
|
||||
}
|
||||
return
|
||||
func printWidgetPlacements(g *gocui.Gui, v *gocui.View) error {
|
||||
w := me.treeRoot.TK.(*guiWidget)
|
||||
w.dumpTree("MM")
|
||||
w.dumpWindows("WW")
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
// simulates a dropdown menu in gocui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
log "go.wit.com/log"
|
||||
"go.wit.com/toolkits/tree"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
// create a new widget in the binary tree
|
||||
func makeNewFlagWidget(wId int) *guiWidget {
|
||||
n := new(tree.Node)
|
||||
n.WidgetType = widget.Flag
|
||||
n.WidgetId = wId
|
||||
n.ParentId = 0
|
||||
|
||||
// store the internal toolkit information
|
||||
tk := new(guiWidget)
|
||||
tk.frame = true
|
||||
|
||||
tk.node = n
|
||||
if tk.node.Parent == nil {
|
||||
tk.node.Parent = me.treeRoot
|
||||
}
|
||||
|
||||
// set the name used by gocui to the id
|
||||
tk.cuiName = fmt.Sprintf("%d DR", wId)
|
||||
|
||||
tk.setColorInput()
|
||||
|
||||
// add this new widget on the binary tree
|
||||
tk.parent = me.treeRoot.TK.(*guiWidget)
|
||||
if tk.parent == nil {
|
||||
panic("makeNewFlagWidget() didn't get treeRoot guiWidget")
|
||||
} else {
|
||||
tk.parent.children = append(tk.parent.children, tk)
|
||||
}
|
||||
|
||||
n.TK = tk
|
||||
return tk
|
||||
}
|
||||
|
||||
func (tk *guiWidget) showDropdown() {
|
||||
if me.dropdown.tk == nil {
|
||||
// should only happen once
|
||||
me.dropdown.tk = makeNewFlagWidget(me.dropdown.wId)
|
||||
me.dropdown.tk.dumpWidget("init() dropdown")
|
||||
}
|
||||
if me.dropdown.tk == nil {
|
||||
log.Log(GOCUI, "showDropdown() Is Broken!")
|
||||
return
|
||||
}
|
||||
|
||||
// todo: fix this after switching to protobuf
|
||||
me.dropdown.items = []string{} // zero out whatever was there before
|
||||
for i, s := range tk.node.Strings() {
|
||||
log.Log(GOCUI, "showDropdown()", tk.String(), i, s)
|
||||
me.dropdown.items = append(me.dropdown.items, s)
|
||||
}
|
||||
log.Log(GOCUI, "new dropdown items should be set to:", me.dropdown.items)
|
||||
|
||||
startW, startH := tk.Position()
|
||||
log.Log(GOCUI, "showDropdown() SHOWING AT W,H=", startW, startH)
|
||||
me.dropdown.tk.Hide()
|
||||
me.dropdown.tk.MoveToOffset(startW+3, startH+2)
|
||||
me.dropdown.tk.labelN = strings.Join(me.dropdown.items, "\n")
|
||||
me.dropdown.tk.Show()
|
||||
me.dropdown.active = true
|
||||
me.dropdown.callerTK = tk
|
||||
|
||||
r := me.dropdown.tk.gocuiSize // set the 'full' size so that mouse clicks are sent here
|
||||
me.dropdown.tk.full.w0 = r.w0
|
||||
me.dropdown.tk.full.w1 = r.w1
|
||||
me.dropdown.tk.full.h0 = r.h0
|
||||
me.dropdown.tk.full.h1 = r.h1
|
||||
|
||||
me.dropdown.tk.dumpWidget("showDropdown()")
|
||||
}
|
||||
|
||||
// if there is a drop down view active, treat it like a dialog box and close it
|
||||
func (w *guiWidget) dropdownClicked(mouseW, mouseH int) string {
|
||||
w.Hide()
|
||||
me.dropdown.active = false
|
||||
|
||||
// only need height to figure out what line in the dropdown menu the user clicked
|
||||
_, startH := w.Position()
|
||||
|
||||
itemNumber := mouseH - startH
|
||||
items := me.dropdown.items
|
||||
// log.Log(GOCUI, "dropdownClicked() look for item", itemNumber, "len(items) =", len(items))
|
||||
if itemNumber < 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
if len(items) >= itemNumber {
|
||||
// log.Log(GOCUI, "dropdownClicked() found", items[itemNumber-1])
|
||||
if items[itemNumber-1] != "" {
|
||||
if me.dropdown.tk != nil {
|
||||
// log.Log(GOCUI, "dropdownClicked() send event for", me.dropdownW.cuiName, me.dropdownW.node.WidgetType)
|
||||
me.dropdown.callerTK.SetText(items[itemNumber-1])
|
||||
me.dropdown.callerTK.node.SetCurrentS(items[itemNumber-1])
|
||||
me.myTree.SendUserEvent(me.dropdown.callerTK.node)
|
||||
}
|
||||
}
|
||||
return items[itemNumber-1]
|
||||
}
|
||||
return ""
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// register how the 'gocui' will work as a GO toolkit plugin
|
||||
// all applications will use these keys. they are universal.
|
||||
|
||||
// tells 'gocui' where to send events
|
||||
func registerHandlers(g *gocui.Gui) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Info("EVENT BINDINGS recovered in r", r)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// mouse handlers
|
||||
g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, mouseDown) // normal left mouse down
|
||||
g.SetKeybinding("", gocui.MouseLeft, gocui.ModMouseCtrl, ctrlDown) // mouse with the ctrl key held down
|
||||
g.SetKeybinding("", gocui.MouseRelease, gocui.ModNone, mouseUp) // mouse button release
|
||||
g.SetKeybinding("", gocui.MouseWheelUp, gocui.ModNone, wheelsUp) // mouse button release
|
||||
g.SetKeybinding("", gocui.MouseWheelDown, gocui.ModNone, wheelsDown) // mouse button release
|
||||
|
||||
// Ctrl key handlers
|
||||
g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, doExit) // CTRL-C : exits the application
|
||||
g.SetKeybinding("", gocui.KeyCtrlV, gocui.ModNone, doPanic) // CTRL-V : force a panic()
|
||||
g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone, openDebuggger) // CTRL-D : open the (D)ebugger
|
||||
keyForced, modForced := gocui.MustParse("ctrl+z") // setup ctrl+z
|
||||
g.SetKeybinding("", keyForced, modForced, handle_ctrl_z) // CTRL-Z :cleverly let's you background gocui (breaks cursor mouse on return)
|
||||
g.SetKeybinding("", gocui.KeyEsc, gocui.ModNone, doEsc) // escape key
|
||||
|
||||
// regular keys
|
||||
g.SetKeybinding("", 'H', gocui.ModNone, theHelp) // 'H' toggles on and off the help menu
|
||||
g.SetKeybinding("", 'O', gocui.ModNone, theStdout) // 'O' toggle the STDOUT window
|
||||
g.SetKeybinding("", 'D', gocui.ModNone, theDarkness) // 'D' toggles light/dark mode
|
||||
g.SetKeybinding("", 'q', gocui.ModNone, doExit) // 'q' exit
|
||||
g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, tabCycleWindows) // '2' use this to test new ideas
|
||||
|
||||
// stdout keys
|
||||
g.SetKeybinding("", gocui.KeyPgup, gocui.ModNone, stdoutPgup) // Pgup scroll up the Stdout buffer
|
||||
g.SetKeybinding("", gocui.KeyPgdn, gocui.ModNone, stdoutPgdn) // Pgdn scroll down the Stdout buffer
|
||||
g.SetKeybinding("", gocui.KeyHome, gocui.ModNone, stdoutHome) // Pgdn scroll down the Stdout buffer
|
||||
g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, stdoutArrowUp) // Pgdn scroll down the Stdout buffer
|
||||
g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, stdoutArrowDown) // Pgdn scroll down the Stdout buffer
|
||||
|
||||
// debugging
|
||||
g.SetKeybinding("", '2', gocui.ModNone, theNotsure) // '2' use this to test new ideas
|
||||
g.SetKeybinding("", 'S', gocui.ModNone, theSuperMouse) // 'S' Super Mouse mode!
|
||||
g.SetKeybinding("", 'M', gocui.ModNone, printWidgetPlacements) // 'M' list all widgets with positions
|
||||
g.SetKeybinding("", 'L', gocui.ModNone, printWidgetTree) // 'L' list all widgets in tree view
|
||||
g.SetKeybinding("", 'f', gocui.ModNone, theFind) // 'f' shows what is under your mouse
|
||||
g.SetKeybinding("", 'd', gocui.ModNone, theLetterD) // 'd' toggles on and off debugging buttons
|
||||
g.SetKeybinding("", 'r', gocui.ModNone, reverseStdout) // 'r' turns scrolling of STDOUT upside down
|
||||
g.SetKeybinding("", 'q', gocui.ModNone, quit) // 'q' only exits gocui. plugin stays alive (?)
|
||||
}
|
||||
|
||||
// flips on 'super mouse' mode // this was awesome for debugging gocui. never remove this code.
|
||||
// while this is turned on, it will print out every widget found under the mouse
|
||||
func theSuperMouse(g *gocui.Gui, v *gocui.View) error {
|
||||
if me.supermouse {
|
||||
log.Log(GOCUI, "supermouse off")
|
||||
me.supermouse = false
|
||||
} else {
|
||||
me.supermouse = true
|
||||
log.Log(GOCUI, "supermouse on")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// use this to test code ideas // put whatever you want here and hit '2' to activate it
|
||||
func theNotsure(g *gocui.Gui, v *gocui.View) error {
|
||||
log.Info("got to theNotsure(). now what? dark =", me.dark)
|
||||
me.refresh = true
|
||||
log.Info("running VerifyParentId()")
|
||||
me.treeRoot.VerifyParentId()
|
||||
/*
|
||||
if me.debug {
|
||||
log.Info("debugging off")
|
||||
me.debug = false
|
||||
} else {
|
||||
log.Info("debugging on")
|
||||
me.debug = true
|
||||
}
|
||||
win := findWindowUnderMouse()
|
||||
if win != nil {
|
||||
win.dumpWidget("found() win. redrawing window:")
|
||||
win.makeWindowActive()
|
||||
}
|
||||
*/
|
||||
return nil
|
||||
}
|
||||
|
||||
func theDarkness(g *gocui.Gui, v *gocui.View) error {
|
||||
if me.dark {
|
||||
me.dark = false
|
||||
log.Info("you have seen the light")
|
||||
} else {
|
||||
me.dark = true
|
||||
log.Info("you have entered into darkness (you may need to trigger SIGWINCH)")
|
||||
log.Info("or maybe open a new window. notsure. This obviously isn't finished.")
|
||||
log.Info("submit patches to this and you will definitely get free cloud credits at WIT")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func wheelsUp(g *gocui.Gui, v *gocui.View) error {
|
||||
stdoutWheelsUp()
|
||||
return nil
|
||||
}
|
||||
|
||||
func wheelsDown(g *gocui.Gui, v *gocui.View) error {
|
||||
stdoutWheelsDown()
|
||||
return nil
|
||||
}
|
||||
|
||||
func tabCycleWindows(g *gocui.Gui, v *gocui.View) error {
|
||||
// log.Info("try to switch windows here")
|
||||
if len(me.allwin) != len(findWindows()) {
|
||||
me.allwin = findWindows()
|
||||
}
|
||||
tk := findNextWindow()
|
||||
if tk == nil {
|
||||
log.Info("findNextWindow() err. returned nil")
|
||||
return nil
|
||||
}
|
||||
tk.makeWindowActive()
|
||||
return nil
|
||||
}
|
||||
|
||||
func doEsc(g *gocui.Gui, v *gocui.View) error {
|
||||
log.Info("got escape key")
|
||||
if me.dropdown.active {
|
||||
me.dropdown.tk.Hide()
|
||||
me.dropdown.active = false
|
||||
log.Info("escaped from dropdown")
|
||||
me.baseGui.SetCurrentView(me.notify.clock.tk.cuiName)
|
||||
}
|
||||
if me.textbox.active {
|
||||
me.textbox.tk.Hide()
|
||||
me.textbox.active = false
|
||||
log.Info("escaped from textbox")
|
||||
me.baseGui.SetCurrentView(me.notify.clock.tk.cuiName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func theShow(g *gocui.Gui, v *gocui.View) error {
|
||||
var w *guiWidget
|
||||
w = me.treeRoot.TK.(*guiWidget)
|
||||
w.showWidgets()
|
||||
return nil
|
||||
}
|
||||
|
||||
func doExit(g *gocui.Gui, v *gocui.View) error {
|
||||
standardExit()
|
||||
return nil
|
||||
}
|
||||
|
||||
func doPanic(g *gocui.Gui, v *gocui.View) error {
|
||||
log.Log(GOCUI, "do panic() here")
|
||||
standardClose()
|
||||
panic("forced panic in gocui")
|
||||
}
|
||||
|
||||
func openDebuggger(g *gocui.Gui, v *gocui.View) error {
|
||||
me.myTree.SendEnableDebugger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func theFind(g *gocui.Gui, v *gocui.View) error {
|
||||
w, h := g.MousePosition()
|
||||
for _, tk := range findByXY(w, h) {
|
||||
// tk.v.BgColor = gocui.ColorGreen
|
||||
tk.dumpWidget("theFind()")
|
||||
// tk.verifyRect()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func reverseStdout(g *gocui.Gui, v *gocui.View) error {
|
||||
if me.stdout.reverse {
|
||||
me.stdout.reverse = false
|
||||
log.Info("stdout scrolling normal")
|
||||
} else {
|
||||
me.stdout.reverse = true
|
||||
log.Info("stdout scrolling is reversed. this is sometimes useful when you")
|
||||
log.Info("only need to see a few most recent lines and have the STDOUT window")
|
||||
log.Info("take up minimal realestate at the bottom of your window")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// is run whenever anyone hits 'd' (in an open space)
|
||||
func theLetterD(g *gocui.Gui, v *gocui.View) error {
|
||||
// widgets that don't have physical existance in
|
||||
// a display toolkit are hidden. In the case
|
||||
// of gocui, they are set as not 'visible' and put offscreen
|
||||
// or have the size set to zero
|
||||
// (hopefully anyway) lots of things with the toolkit
|
||||
// still don't work
|
||||
|
||||
fakeStartWidth = me.FakeW
|
||||
fakeStartHeight = me.TabH + me.FramePadH
|
||||
if me.showDebug {
|
||||
showFake()
|
||||
me.showDebug = false
|
||||
} else {
|
||||
hideFake()
|
||||
me.showDebug = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func theHelp(g *gocui.Gui, v *gocui.View) error {
|
||||
if me.showHelp {
|
||||
log.Info("Show the help!")
|
||||
showHelp()
|
||||
} else {
|
||||
log.Info("Hide the help!")
|
||||
hideHelp()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// todo: find and give credit to the person that I found this patch in their forked repo
|
||||
// handle ctrl+z
|
||||
func handle_ctrl_z(g *gocui.Gui, v *gocui.View) error {
|
||||
gocui.Suspend()
|
||||
log.Info("got ctrl+z")
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGSTOP)
|
||||
log.Info("got ctrl+z syscall() done")
|
||||
gocui.Resume()
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/toolkits/tree"
|
||||
)
|
||||
|
||||
func theStdout(g *gocui.Gui, v *gocui.View) error {
|
||||
// me.stdout.pager = 0
|
||||
infos := fmt.Sprintf("pager=%d len(%d) ", me.stdout.pager, len(me.stdout.outputS))
|
||||
infos += fmt.Sprintf("last(%d,%d)", me.stdout.lastW, me.stdout.lastH)
|
||||
me.stdout.changed = true
|
||||
|
||||
if me.stdout.outputOnTop {
|
||||
if me.stdout.outputOffscreen {
|
||||
me.stdout.outputOffscreen = false
|
||||
log.Info("stdout moved off screen", infos)
|
||||
me.stdout.lastW = me.stdout.tk.gocuiSize.w0
|
||||
me.stdout.lastH = me.stdout.tk.gocuiSize.h0
|
||||
relocateStdoutOffscreen()
|
||||
new1 := new(tree.ToolkitConfig)
|
||||
new1.Plugin = "gocui"
|
||||
new1.Name = "stdoutoffscreen"
|
||||
new1.Value = "true"
|
||||
me.myTree.ConfigSave(new1)
|
||||
return nil
|
||||
} else {
|
||||
me.stdout.outputOffscreen = true
|
||||
log.Info("stdout moved on screen", infos)
|
||||
new1 := new(tree.ToolkitConfig)
|
||||
new1.Plugin = "gocui"
|
||||
new1.Name = "stdoutoffscreen"
|
||||
new1.Value = "false"
|
||||
me.myTree.ConfigSave(new1)
|
||||
}
|
||||
// move the stdout window back onscreen
|
||||
me.stdout.tk.relocateStdout(me.stdout.lastW, me.stdout.lastH)
|
||||
me.stdout.outputOnTop = false
|
||||
setThingsOnTop()
|
||||
new1 := new(tree.ToolkitConfig)
|
||||
new1.Plugin = "gocui"
|
||||
new1.Name = "stdoutlevel"
|
||||
new1.Value = "bottom"
|
||||
me.myTree.ConfigSave(new1)
|
||||
} else {
|
||||
me.stdout.outputOnTop = true
|
||||
setThingsOnTop()
|
||||
new1 := new(tree.ToolkitConfig)
|
||||
new1.Plugin = "gocui"
|
||||
new1.Name = "stdoutlevel"
|
||||
new1.Value = "top"
|
||||
me.myTree.ConfigSave(new1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stdoutPgup(g *gocui.Gui, v *gocui.View) error {
|
||||
me.stdout.pager -= me.stdout.Height() - 2
|
||||
if me.stdout.pager < 0 {
|
||||
me.stdout.pager = 0
|
||||
}
|
||||
tk := me.stdout.tk
|
||||
tk.refreshStdout()
|
||||
return nil
|
||||
}
|
||||
|
||||
func stdoutHome(g *gocui.Gui, v *gocui.View) error {
|
||||
me.stdout.pager = 0
|
||||
me.stdout.tk.refreshStdout()
|
||||
return nil
|
||||
}
|
||||
|
||||
func stdoutArrowUp(g *gocui.Gui, v *gocui.View) error {
|
||||
if me.stdout.reverse {
|
||||
stdoutWheelsDown()
|
||||
} else {
|
||||
stdoutWheelsUp()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stdoutArrowDown(g *gocui.Gui, v *gocui.View) error {
|
||||
if me.stdout.reverse {
|
||||
stdoutWheelsUp()
|
||||
} else {
|
||||
stdoutWheelsDown()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stdoutPgdn(g *gocui.Gui, v *gocui.View) error {
|
||||
win := findWindowUnderMouse()
|
||||
if win != nil {
|
||||
if win.full.Height() > 50 {
|
||||
log.Info("paging through really large window pager =", win.window.pager)
|
||||
win.window.pager += 10
|
||||
return nil
|
||||
}
|
||||
}
|
||||
me.stdout.pager += 10
|
||||
|
||||
tk := me.stdout.tk
|
||||
tk.refreshStdout()
|
||||
return nil
|
||||
}
|
||||
|
||||
// scrolling up with the mouse wheel (or trackpad)
|
||||
func stdoutWheelsUp() {
|
||||
// log.Info("private wheels up")
|
||||
me.stdout.pager -= 2
|
||||
if me.stdout.pager < 0 {
|
||||
me.stdout.pager = 0
|
||||
}
|
||||
me.stdout.tk.refreshStdout()
|
||||
}
|
||||
|
||||
// scrolling down with the mouse wheel (or trackpad)
|
||||
func stdoutWheelsDown() {
|
||||
// log.Info("you've landed")
|
||||
me.stdout.pager += 2
|
||||
if me.stdout.pager > len(me.stdout.outputS) {
|
||||
me.stdout.pager = len(me.stdout.outputS)
|
||||
}
|
||||
me.stdout.tk.refreshStdout()
|
||||
}
|
|
@ -1,45 +1,16 @@
|
|||
// Copyright 2014 The gocui Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"fmt"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// This initializes the gocui package
|
||||
// it runs SetManagerFunc which passes every input
|
||||
// event (keyboard, mouse, etc) to the function "gocuiEvent()"
|
||||
func gocuiMain() {
|
||||
g, err := gocui.NewGui(gocui.OutputNormal, true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer g.Close()
|
||||
|
||||
me.baseGui = g
|
||||
|
||||
g.Cursor = true
|
||||
g.Mouse = true
|
||||
|
||||
// this sets the function that is run on every event. For example:
|
||||
// When you click the mouse, move the mouse, or press a key on the keyboard
|
||||
// This is equivalent to xev or similar to cat /dev/input on linux
|
||||
g.SetManagerFunc(gocuiEvent)
|
||||
|
||||
if err := defaultKeybindings(g); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := g.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Thanks to the gocui developers -- your package kicks ass
|
||||
// This function is called on every event. It is a callback function from the gocui package
|
||||
// which has an excellent implementation. While gocui handles things like text highlighting
|
||||
|
@ -47,29 +18,11 @@ func gocuiMain() {
|
|||
// complicated console handling, it sends events here in a clean way.
|
||||
// This is equivalent to the linux command xev (apt install x11-utils)
|
||||
func gocuiEvent(g *gocui.Gui) error {
|
||||
maxX, maxY := g.Size()
|
||||
mx, my := g.MousePosition()
|
||||
log.Log(NOW, "handleEvent() START", maxX, maxY, mx, my, msgMouseDown)
|
||||
if _, err := g.View("msg"); msgMouseDown && err == nil {
|
||||
moveMsg(g)
|
||||
}
|
||||
if widgetView, _ := g.View("msg"); widgetView == nil {
|
||||
log.Log(NOW, "handleEvent() create output widget now", maxX, maxY, mx, my)
|
||||
makeOutputWidget(g, "this is a create before a mouse click")
|
||||
if me.logStdout != nil {
|
||||
// setOutput(me.logStdout)
|
||||
}
|
||||
} else {
|
||||
log.Log(INFO, "output widget already exists", maxX, maxY, mx, my)
|
||||
}
|
||||
me.ecount += 1
|
||||
mouseMove(g)
|
||||
log.Log(INFO, "handleEvent() END ", maxX, maxY, mx, my, msgMouseDown)
|
||||
return nil
|
||||
}
|
||||
|
||||
func dragOutputWindow() {
|
||||
}
|
||||
|
||||
// turns off the frame on the global window
|
||||
func setFrame(b bool) {
|
||||
// TODO: figure out what this might be useful for
|
||||
|
@ -81,10 +34,42 @@ func setFrame(b bool) {
|
|||
v.Frame = b
|
||||
}
|
||||
|
||||
// a test. exits gocui, but the application still runs
|
||||
// maybe can switch toolkits?
|
||||
func quit(g *gocui.Gui, v *gocui.View) error {
|
||||
return gocui.ErrQuit
|
||||
}
|
||||
|
||||
func (tk *guiWidget) SetView() error {
|
||||
if me.baseGui == nil {
|
||||
return fmt.Errorf("me.baseGui == nil")
|
||||
}
|
||||
|
||||
r := new(rectType)
|
||||
r.w0 = tk.gocuiSize.w0
|
||||
r.h0 = tk.gocuiSize.h0
|
||||
r.w1 = tk.gocuiSize.w1
|
||||
r.h1 = tk.gocuiSize.h1
|
||||
|
||||
return tk.SetViewRect(r)
|
||||
}
|
||||
|
||||
func (tk *guiWidget) SetViewRect(r *rectType) error {
|
||||
if me.baseGui == nil {
|
||||
return fmt.Errorf("me.baseGui == nil")
|
||||
}
|
||||
|
||||
var err error
|
||||
tk.v, err = me.baseGui.SetView(tk.cuiName, r.w0, r.h0, r.w1, r.h1, 0)
|
||||
if err != nil {
|
||||
if !errors.Is(err, gocui.ErrUnknownView) {
|
||||
log.Log(ERROR, "SetView() global failed on name =", tk.cuiName)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetView(name string, x0, y0, x1, y1 int, overlaps byte) *gocui.View {
|
||||
if me.baseGui == nil {
|
||||
log.Log(ERROR, "SetView() ERROR: me.baseGui == nil")
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func mouseUp(g *gocui.Gui, v *gocui.View) error {
|
||||
// useful to debug everything that is being clicked on
|
||||
/*
|
||||
for _, tk := range findByXY(w, h) {
|
||||
tk.dumpWidget("mouseUp()")
|
||||
}
|
||||
*/
|
||||
|
||||
me.mouse.mouseUp = true
|
||||
me.mouse.currentDrag = nil
|
||||
|
||||
if me.mouse.double && (time.Since(me.mouse.down) < me.mouse.doubletime) {
|
||||
me.mouse.double = false
|
||||
doMouseDoubleClick(me.mouse.downW, me.mouse.downH)
|
||||
return nil
|
||||
}
|
||||
me.mouse.double = false
|
||||
|
||||
if time.Since(me.mouse.down) < me.mouse.clicktime {
|
||||
doMouseClick(me.mouse.downW, me.mouse.downH)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// this is where you have to figure out what
|
||||
// widget was underneath so you can active
|
||||
// the right response for the toolkit user's app
|
||||
func mouseDown(g *gocui.Gui, v *gocui.View) error {
|
||||
if me.mouse.mouseUp {
|
||||
if time.Since(me.mouse.down) < me.mouse.doubletime {
|
||||
me.mouse.double = true
|
||||
}
|
||||
me.mouse.mouseUp = false
|
||||
me.mouse.down = time.Now()
|
||||
w, h := g.MousePosition()
|
||||
me.mouse.downW = w
|
||||
me.mouse.downH = h
|
||||
|
||||
win := findWindowUnderMouse()
|
||||
if win != nil {
|
||||
w, h := g.MousePosition()
|
||||
s := fmt.Sprintf("mouse(%d,%d) ", w, h)
|
||||
offW := win.full.w1 - w
|
||||
offH := win.full.h1 - h
|
||||
s += fmt.Sprintf("corner(%d,%d)", offW, offH)
|
||||
if (offW < 3) && (offH < 3) {
|
||||
log.Info("attempting resize on ", s, win.cuiName)
|
||||
me.mouse.resize = true
|
||||
// store the stdout corner for computing the drag size
|
||||
me.stdout.lastW = me.stdout.tk.gocuiSize.w0
|
||||
me.stdout.lastH = me.stdout.tk.gocuiSize.h0
|
||||
} else {
|
||||
// log.Info("mouse down resize off", s)
|
||||
me.mouse.resize = false
|
||||
}
|
||||
win.setAsDragging()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func (tk *guiWidget) doButtonClick() {
|
||||
if tk.IsEnabled() {
|
||||
tk.dumpWidget("click()") // enable this to debug widget clicks
|
||||
me.myTree.SendFromUser(tk.node)
|
||||
} else {
|
||||
log.Info("button is currently disabled by the application")
|
||||
// tk.dumpWidget("disabled()") // enable this to debug widget clicks
|
||||
}
|
||||
}
|
||||
|
||||
// handles a mouse click
|
||||
func doMouseClick(w int, h int) {
|
||||
// Flag widgets (dropdown menus, etc) are the highest priority. ALWAYS SEND MOUSE CLICKS THERE FIRST
|
||||
// handle an open dropdown menu or text entry window first
|
||||
if me.dropdown.active || me.textbox.active {
|
||||
// can't drag or do anything when dropdown or textbox are visible
|
||||
for _, tk := range findByXY(w, h) {
|
||||
if tk.WidgetId() == me.dropdown.wId {
|
||||
log.Info("got dropdown click", w, h, tk.cuiName)
|
||||
tk.dropdownClicked(w, h)
|
||||
return
|
||||
}
|
||||
if tk.WidgetId() == me.textbox.wId {
|
||||
log.Info("got textbox click", w, h, tk.cuiName)
|
||||
textboxClosed()
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Info("a dropdown or textbox is active. you can't click anywhere else (otherwise hit ESC)", w, h)
|
||||
return
|
||||
}
|
||||
|
||||
win := findWindowUnderMouse()
|
||||
if win == nil {
|
||||
log.Log(INFO, "click() nothing was at:", w, h)
|
||||
log.Log(INFO, "click() check if", w, h, "is the libnotify icon")
|
||||
if me.notify.icon.tk != nil && me.notify.icon.tk.gocuiSize.inRect(w, h) {
|
||||
log.Log(GOCUI, "click() is libnotify.icon!")
|
||||
if me.notify.icon.active {
|
||||
log.Info("show notify.icon here")
|
||||
setNotifyIconText("[X]")
|
||||
me.notify.icon.active = false
|
||||
} else {
|
||||
log.Info("hide notify.icon here")
|
||||
setNotifyIconText("[ ]")
|
||||
me.notify.icon.active = true
|
||||
}
|
||||
return
|
||||
}
|
||||
if me.notify.clock.tk != nil && me.notify.clock.tk.gocuiSize.inRect(w, h) {
|
||||
log.Log(GOCUI, "click() is the clock!")
|
||||
if me.showHelp {
|
||||
log.Info("show help")
|
||||
showHelp()
|
||||
} else {
|
||||
log.Info("hide help")
|
||||
hideHelp()
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
if !win.isWindowActive() {
|
||||
win.makeWindowActive()
|
||||
return
|
||||
} else {
|
||||
// potentally the user is closing the window
|
||||
if win.checkWindowClose(w, h) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// look in this window for widgets
|
||||
// widgets have priority. send the click here first
|
||||
for _, tk := range win.findByXYreal(w, h) {
|
||||
switch tk.WidgetType() {
|
||||
case widget.Checkbox:
|
||||
if tk.Checked() {
|
||||
log.Log(WARN, "checkbox is being set to false")
|
||||
tk.SetChecked(false)
|
||||
tk.setCheckbox()
|
||||
} else {
|
||||
log.Log(WARN, "checkbox is being set to true")
|
||||
tk.SetChecked(true)
|
||||
tk.setCheckbox()
|
||||
}
|
||||
me.myTree.SendUserEvent(tk.node)
|
||||
return
|
||||
case widget.Button:
|
||||
tk.doButtonClick()
|
||||
return
|
||||
case widget.Combobox:
|
||||
tk.showDropdown()
|
||||
return
|
||||
case widget.Dropdown:
|
||||
tk.showDropdown()
|
||||
return
|
||||
case widget.Textbox:
|
||||
log.Log(WARN, "TODO: textbox click")
|
||||
tk.prepTextbox()
|
||||
return
|
||||
case widget.Label:
|
||||
if tk.node.InTable() {
|
||||
if tk.node.State.AtH == 0 {
|
||||
log.Log(NOW, "todo: sort by column here")
|
||||
tk.dumpWidget("sort")
|
||||
}
|
||||
}
|
||||
return
|
||||
default:
|
||||
// TODO: enable the GUI debugger in gocui
|
||||
// tk.dumpWidget("undef click()") // enable this to debug widget clicks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo: use this?
|
||||
func ctrlDown(g *gocui.Gui, v *gocui.View) error {
|
||||
log.Info("todo: clicked with ctrlDown")
|
||||
return nil
|
||||
}
|
||||
|
||||
func doMouseDoubleClick(w int, h int) {
|
||||
me.mouse.double = false
|
||||
// log.Printf("actually a double click (%d,%d)", w, h)
|
||||
|
||||
if me.dropdown.active || me.textbox.active {
|
||||
// can't drag or do anything when dropdown or textbox are visible
|
||||
log.Info("can't double click. dropdown or textbox is active")
|
||||
return
|
||||
}
|
||||
|
||||
for _, tk := range findByXY(w, h) {
|
||||
if tk.WidgetType() == widget.Window {
|
||||
tk.makeWindowActive()
|
||||
return
|
||||
}
|
||||
|
||||
if tk.WidgetType() == widget.Stdout {
|
||||
if me.stdout.outputOnTop {
|
||||
me.stdout.outputOnTop = false
|
||||
setThingsOnTop()
|
||||
} else {
|
||||
me.stdout.outputOnTop = true
|
||||
setThingsOnTop()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
// 2025 note by jcarr:
|
||||
// this is one of the coolest things ever worked with.
|
||||
// Personally, I've been working on making a gocui GO plugin
|
||||
// so I can use it as a generalized console GUI toolkit.
|
||||
//
|
||||
// Well done everyone that has contributed to this gocui project !!!
|
||||
// I am in your debt. Happy hacking & peace.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
log "go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
// this function uses the mouse position to highlight & unhighlight things
|
||||
// this is run every time the user moves the mouse over the terminal window
|
||||
func mouseMove(g *gocui.Gui) {
|
||||
// this runs while the user moves the mouse. this highlights text
|
||||
// toggle off all highlight views except for whatever is under the mouse
|
||||
// START HIGHLIGHTING
|
||||
for _, view := range g.Views() {
|
||||
view.Highlight = false
|
||||
}
|
||||
w, h := g.MousePosition()
|
||||
// TODO: try to highlight entire grid rows
|
||||
if v, err := g.ViewByPosition(w, h); err == nil {
|
||||
// block anything from highlighting while a dialog box is open
|
||||
if me.dropdown.active || me.textbox.active {
|
||||
if me.dropdown.tk != nil && me.dropdown.tk.v == v {
|
||||
v.Highlight = true
|
||||
}
|
||||
if me.textbox.tk != nil && me.textbox.tk.v == v {
|
||||
v.Highlight = true
|
||||
}
|
||||
} else {
|
||||
v.Highlight = true
|
||||
}
|
||||
}
|
||||
|
||||
// old hack. create the 'msg' view if it does not yet exist
|
||||
// TODO: put this somewhere more correct
|
||||
if widgetView, _ := g.View("msg"); widgetView == nil {
|
||||
me.stdout.changed = true
|
||||
if createStdout(g) {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// END HIGHLIGHTING
|
||||
|
||||
// Super Mouse Mode. very useful for debugging in the past. also, just fun
|
||||
if me.supermouse {
|
||||
w, h := g.MousePosition()
|
||||
for _, tk := range findByXY(w, h) {
|
||||
s := fmt.Sprintf("SM (%3d,%3d)", w, h)
|
||||
tk.dumpWidget(s)
|
||||
}
|
||||
}
|
||||
|
||||
if me.mouse.mouseUp {
|
||||
return
|
||||
}
|
||||
// EVERYTHING BELOW THIS IS RELATED TO MOUSE DRAGGING
|
||||
|
||||
// has the mouse been pressed down long enough to start dragging?
|
||||
if time.Since(me.mouse.down) < me.mouse.clicktime {
|
||||
// not dragging
|
||||
return
|
||||
}
|
||||
|
||||
if me.dropdown.active || me.textbox.active {
|
||||
// can't drag
|
||||
return
|
||||
}
|
||||
|
||||
// drag whatever was set to drag
|
||||
if me.mouse.currentDrag != nil {
|
||||
// me.mouse.currentDrag.dumpWidget(fmt.Sprintf("MM (%3d,%3d)", w, h))
|
||||
me.mouse.currentDrag.moveNew()
|
||||
return
|
||||
}
|
||||
log.Info(fmt.Sprintf("gocui gui toolkit plugin error. nothing to drag at (%d,%d)", w, h))
|
||||
return
|
||||
}
|
||||
|
||||
func (tk *guiWidget) setAsDragging() {
|
||||
me.mouse.currentDrag = tk
|
||||
tk.lastW = tk.gocuiSize.w0
|
||||
tk.lastH = tk.gocuiSize.h0
|
||||
}
|
||||
|
||||
// this is how the window gets dragged around
|
||||
func (tk *guiWidget) moveNew() {
|
||||
w, h := me.baseGui.MousePosition()
|
||||
if tk.WidgetType() == widget.Window {
|
||||
tk.window.wasDragged = true
|
||||
|
||||
// compute the new location based off how far the mouse has moved
|
||||
// since the mouse button was pressed down
|
||||
tk.gocuiSize.w0 = tk.lastW + w - me.mouse.downW
|
||||
tk.gocuiSize.h0 = tk.lastH + h - me.mouse.downH
|
||||
tk.makeWindowActive()
|
||||
return
|
||||
}
|
||||
|
||||
if tk.WidgetType() == widget.Stdout {
|
||||
if me.mouse.resize {
|
||||
newW := w - me.stdout.lastW
|
||||
newH := h - me.stdout.lastH
|
||||
me.stdout.w = newW
|
||||
me.stdout.h = newH
|
||||
// log.Info("Resize true", w, h, newW, newH)
|
||||
// me.stdout.lastW = w - me.stdout.mouseOffsetW
|
||||
// me.stdout.lastH = h - me.stdout.mouseOffsetH
|
||||
tk.relocateStdout(me.stdout.lastW, me.stdout.lastH)
|
||||
} else {
|
||||
// compute the new location based off how far the mouse has moved
|
||||
// since the mouse button was pressed down
|
||||
newW := tk.lastW + w - me.mouse.downW
|
||||
newH := tk.lastH + h - me.mouse.downH
|
||||
tk.relocateStdout(newW, newH)
|
||||
// log.Info("Resize false", w, h, newW, newH)
|
||||
}
|
||||
setThingsOnTop() // sets help, Stdout, etc on the top after windows have been redrawn
|
||||
me.stdout.changed = true
|
||||
}
|
||||
}
|
58
fakefile.go
58
fakefile.go
|
@ -1,58 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
type FakeFile struct {
|
||||
reader *bytes.Reader
|
||||
buffer *bytes.Buffer
|
||||
offset int64
|
||||
}
|
||||
|
||||
func (f *FakeFile) Read(p []byte) (n int, err error) {
|
||||
n, err = f.reader.ReadAt(p, f.offset)
|
||||
f.offset += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (f *FakeFile) Write(p []byte) (n int, err error) {
|
||||
n, err = f.buffer.Write(p)
|
||||
f.offset += int64(n)
|
||||
f.reader.Reset(f.buffer.Bytes())
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (f *FakeFile) Seek(offset int64, whence int) (int64, error) {
|
||||
newOffset := f.offset
|
||||
|
||||
switch whence {
|
||||
case io.SeekStart:
|
||||
newOffset = offset
|
||||
case io.SeekCurrent:
|
||||
newOffset += offset
|
||||
case io.SeekEnd:
|
||||
newOffset = int64(f.buffer.Len()) + offset
|
||||
default:
|
||||
return 0, errors.New("Seek: whence not at start,current or end")
|
||||
}
|
||||
// never can get here right?
|
||||
|
||||
if newOffset < 0 {
|
||||
return 0, errors.New("Seek: offset < 0")
|
||||
}
|
||||
|
||||
f.offset = newOffset
|
||||
return f.offset, nil
|
||||
}
|
||||
|
||||
func NewFakeFile() *FakeFile {
|
||||
buf := &bytes.Buffer{}
|
||||
return &FakeFile{
|
||||
reader: bytes.NewReader(buf.Bytes()),
|
||||
buffer: buf,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
log "go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
/*
|
||||
gocui defines the offset like this:
|
||||
|
||||
width -> increases to the right
|
||||
---------------------------------- hieght
|
||||
| H = 1 | increases
|
||||
| | |
|
||||
| W = 1 W = 18 | |
|
||||
| | v
|
||||
| H = 5 | downwards
|
||||
-------------------------------------
|
||||
*/
|
||||
|
||||
// change over to this name
|
||||
// returns all the widgets under (X,H) that are visible
|
||||
func findByXY(w int, h int) []*guiWidget {
|
||||
rootW := me.treeRoot.TK.(*guiWidget)
|
||||
|
||||
// this searches the binary tree recursively (function is right below)
|
||||
return rootW.findByXYreal(w, h)
|
||||
}
|
||||
|
||||
func (r rectType) inRect(w int, h int) bool {
|
||||
if (r.w0 <= w) && (w <= r.w1) && (r.h0 <= h) && (h <= r.h1) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// this checks a widget to see if it is under (W,H), then checks the widget's children
|
||||
// anything that matches is passed back as an array of widgets
|
||||
func (tk *guiWidget) findByXYreal(w int, h int) []*guiWidget {
|
||||
var widgets []*guiWidget
|
||||
|
||||
// if !tk.Visible() {
|
||||
// ignore widgets that are not visible
|
||||
// } else {
|
||||
|
||||
// check the location to see if this is under (W,H)
|
||||
// if it is, return this widget
|
||||
// if (tk.gocuiSize.w0 <= w) && (w <= tk.gocuiSize.w1) &&
|
||||
// (tk.gocuiSize.h0 <= h) && (h <= tk.gocuiSize.h1) {
|
||||
// if tk.gocuiSize.inRect(w, h) {
|
||||
// widgets = append(widgets, tk)
|
||||
// } else {
|
||||
// if (tk.full.w0 <= w) && (w <= tk.full.w1) &&
|
||||
// (tk.full.h0 <= h) && (h <= tk.full.h1) {
|
||||
if tk.full.inRect(w, h) {
|
||||
widgets = append(widgets, tk)
|
||||
}
|
||||
// log.Log(GOCUI, "findByXY() found", widget.WidgetType(), w, h)
|
||||
// }
|
||||
// }
|
||||
// tk.verifyRect()
|
||||
|
||||
// search through the children widgets in the binary tree
|
||||
for _, child := range tk.children {
|
||||
widgets = append(widgets, child.findByXYreal(w, h)...)
|
||||
}
|
||||
|
||||
return widgets
|
||||
}
|
||||
|
||||
// returns all the windows from the root of the binary tree
|
||||
func findWindows() []*guiWidget {
|
||||
rootW := me.treeRoot.TK.(*guiWidget)
|
||||
return rootW.findWindows()
|
||||
}
|
||||
|
||||
// walk the binary tree looking for WidgetType == Window
|
||||
func (tk *guiWidget) findWindows() []*guiWidget {
|
||||
var found []*guiWidget
|
||||
|
||||
if tk.WidgetType() == widget.Window {
|
||||
found = append(found, tk)
|
||||
}
|
||||
|
||||
for _, child := range tk.children {
|
||||
found = append(found, child.findWindows()...)
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
// used by gocui.TabKey to rotate through the windows
|
||||
func findNextWindow() *guiWidget {
|
||||
var found bool
|
||||
if len(me.allwin) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, tk := range me.allwin {
|
||||
if tk.window.active {
|
||||
found = true
|
||||
continue
|
||||
}
|
||||
if found {
|
||||
return tk
|
||||
}
|
||||
}
|
||||
// at the end, loop to the beginning
|
||||
return me.allwin[0]
|
||||
}
|
||||
|
||||
// find the window under the mouse and only the window under the mouse
|
||||
func findWindowUnderMouse() *guiWidget {
|
||||
w, h := me.baseGui.MousePosition()
|
||||
|
||||
if len(me.allwin) != len(findWindows()) {
|
||||
me.allwin = findWindows()
|
||||
}
|
||||
|
||||
// if the stdout window is on top, check it first
|
||||
if me.stdout.outputOnTop {
|
||||
if me.stdout.tk.full.inRect(w, h) {
|
||||
// log.Info(fmt.Sprintf("findWindowUnderMouse() found %s stdout on top (%dx%d)", me.stdout.tk.cuiName, w, h))
|
||||
return me.stdout.tk
|
||||
}
|
||||
}
|
||||
|
||||
// now check if the active window is below the mouse
|
||||
for _, tk := range me.allwin {
|
||||
if tk.window.active {
|
||||
if tk.full.inRect(w, h) {
|
||||
// log.Info(fmt.Sprintf("findWindowUnderMouse() found %s active window (%dx%d)", tk.cuiName, w, h))
|
||||
return tk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// well, just find any window then
|
||||
// sorting by order might work?
|
||||
slices.SortFunc(me.allwin, func(a, b *guiWidget) int {
|
||||
return a.window.order - b.window.order
|
||||
})
|
||||
|
||||
for _, win := range me.allwin {
|
||||
if win.full.inRect(w, h) {
|
||||
// log.Info(fmt.Sprintf("findWindowUnderMouse() found %s window (%dx%d)", win.cuiName, w, h))
|
||||
return win
|
||||
}
|
||||
}
|
||||
|
||||
// okay, no window. maybe the stdout is there?
|
||||
if me.stdout.tk.full.inRect(w, h) {
|
||||
// log.Info(fmt.Sprintf("findWindowUnderMouse() found %s stdout (%dx%d)", me.stdout.tk.cuiName, w, h))
|
||||
return me.stdout.tk
|
||||
}
|
||||
|
||||
// geez. nothing! maybe auto return stdout?
|
||||
log.Info("findWindowUnderMouse() no window found at", w, h)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tk *guiWidget) findParentWindow() *guiWidget {
|
||||
if tk.WidgetType() == widget.Window {
|
||||
return tk
|
||||
}
|
||||
if tk.parent == nil {
|
||||
return nil
|
||||
}
|
||||
return tk.parent.findParentWindow()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) findWidgetByName(name string) *guiWidget {
|
||||
if tk.cuiName == name {
|
||||
return tk
|
||||
}
|
||||
for _, child := range tk.children {
|
||||
found := child.findWidgetByName(name)
|
||||
if found != nil {
|
||||
return found
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tk *guiWidget) findWidgetByView(v *gocui.View) *guiWidget {
|
||||
if tk.v == v {
|
||||
return tk
|
||||
}
|
||||
if tk.cuiName == v.Name() {
|
||||
log.Log(NOW, "findWidget() error. names are mismatched or out of sync", tk.cuiName)
|
||||
log.Log(NOW, "findWidget() or maybe the view has been deleted")
|
||||
// return tk
|
||||
}
|
||||
for _, child := range tk.children {
|
||||
found := child.findWidgetByView(v)
|
||||
if found != nil {
|
||||
return found
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tk *guiWidget) findWidgetById(id int) *guiWidget {
|
||||
if tk.WidgetId() == id {
|
||||
return tk
|
||||
}
|
||||
for _, child := range tk.children {
|
||||
found := child.findWidgetById(id)
|
||||
if found != nil {
|
||||
return found
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
21
go.mod
21
go.mod
|
@ -1,21 +0,0 @@
|
|||
module go.wit.com/toolkits/gocui
|
||||
|
||||
go 1.21.4
|
||||
|
||||
require (
|
||||
github.com/awesome-gocui/gocui v1.1.0
|
||||
go.wit.com/log v0.5.5
|
||||
go.wit.com/widget v1.1.5
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gdamore/encoding v1.0.0 // indirect
|
||||
github.com/gdamore/tcell/v2 v2.4.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.10 // indirect
|
||||
github.com/rivo/uniseg v0.1.0 // indirect
|
||||
go.wit.com/dev/davecgh/spew v1.1.4 // indirect
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
||||
golang.org/x/text v0.3.3 // indirect
|
||||
)
|
26
go.sum
26
go.sum
|
@ -1,26 +0,0 @@
|
|||
github.com/awesome-gocui/gocui v1.1.0 h1:db2j7yFEoHZjpQFeE2xqiatS8bm1lO3THeLwE6MzOII=
|
||||
github.com/awesome-gocui/gocui v1.1.0/go.mod h1:M2BXkrp7PR97CKnPRT7Rk0+rtswChPtksw/vRAESGpg=
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell/v2 v2.4.0 h1:W6dxJEmaxYvhICFoTY3WrLLEXsQ11SaFnKGVEXW57KM=
|
||||
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
go.wit.com/dev/davecgh/spew v1.1.4 h1:C9hj/rjlUpdK+E6aroyLjCbS5MFcyNUOuP1ICLWdNek=
|
||||
go.wit.com/dev/davecgh/spew v1.1.4/go.mod h1:sihvWmnQ/09FWplnEmozt90CCVqBtGuPXM811tgfhFA=
|
||||
go.wit.com/log v0.5.5 h1:bK3b94uVKgev4jB5wg06FnvCFBEapQICTSH2YW+CWr4=
|
||||
go.wit.com/log v0.5.5/go.mod h1:BaJBfHFqcJSJLXGQ9RHi3XVhPgsStxSMZRlaRxW4kAo=
|
||||
go.wit.com/widget v1.1.5 h1:jx5hJ2WLZJnCcvMuaLHegzpNlzwo+0kOkzsRkzRiB30=
|
||||
go.wit.com/widget v1.1.5/go.mod h1:I8tnD3x3ECbB/CRNnLCdC+uoyk7rK0AEkzK1bQYSqoQ=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
85
help.go
85
help.go
|
@ -1,4 +1,7 @@
|
|||
// Copyright 2014 The gocui Authors. All rights reserved.
|
||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
// Prior Copyright 2014 The gocui Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
|
@ -10,33 +13,51 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
log "go.wit.com/log"
|
||||
)
|
||||
|
||||
var helpText []string = []string{"KEYBINDINGS",
|
||||
/*
|
||||
This in helpText doesn't print
|
||||
|
||||
"\x1b[0;32m \x1b[0m", // this was a test to see what might be
|
||||
// possible with gocui. it doesn't seem to work for me
|
||||
*/
|
||||
|
||||
var helpText []string = []string{"Help Menu",
|
||||
"",
|
||||
"?: toggle help",
|
||||
"d: toggle debugging",
|
||||
"r: redraw widgets",
|
||||
"s/h: show/hide all widgets",
|
||||
"L: list all widgets",
|
||||
"M: list all widgets positions",
|
||||
"q: quit()",
|
||||
"p: panic()",
|
||||
"o: show Stdout",
|
||||
"l: log to /tmp/witgui.log",
|
||||
"Ctrl-D: Toggle Debugging",
|
||||
"Ctrl-V: Toggle Verbose Debugging",
|
||||
"Ctrl-C: Exit",
|
||||
"Tab toggle through windows",
|
||||
"'O' toggle STDOUT",
|
||||
"'H' toggle this gocui menu",
|
||||
"'D' toggle light/dark mode",
|
||||
"CTRL-z background to shell",
|
||||
"CTRL-c quit()",
|
||||
"",
|
||||
"Debugging:",
|
||||
"'S' Supermouse mode",
|
||||
"'M' list all widget positions",
|
||||
"'L' list all widgets in tree",
|
||||
"<Pgup> scroll up the STDOUT window",
|
||||
"<Pgdn> scroll down the STDOUT window",
|
||||
"'r' reverse STDOUT scrolling",
|
||||
}
|
||||
|
||||
func hidehelplayout() {
|
||||
func hideHelp() {
|
||||
if me.showHelp {
|
||||
log.Info("help is already down")
|
||||
me.showHelp = true
|
||||
return
|
||||
}
|
||||
me.showHelp = true
|
||||
me.baseGui.DeleteView("help")
|
||||
// n.deleteView()
|
||||
// child.hideFake()
|
||||
}
|
||||
|
||||
func helplayout() error {
|
||||
func showHelp() error {
|
||||
if !me.showHelp {
|
||||
log.Info("help is already up")
|
||||
me.showHelp = false
|
||||
return nil
|
||||
}
|
||||
me.showHelp = false
|
||||
g := me.baseGui
|
||||
var err error
|
||||
maxX, _ := g.Size()
|
||||
|
@ -48,17 +69,18 @@ func helplayout() error {
|
|||
}
|
||||
}
|
||||
|
||||
help, err := g.SetView("help", maxX-(newW+me.FramePadW), 0, maxX-1, len(helpText)+me.FramePadH, 0)
|
||||
a := maxX - (newW + me.FramePadW)
|
||||
b := me.notify.help.offsetH
|
||||
c := maxX - 1
|
||||
d := me.notify.help.offsetH + len(helpText) + me.FramePadH
|
||||
|
||||
help, err := g.SetView("help", a, b, c, d, 0)
|
||||
if err != nil {
|
||||
if !errors.Is(err, gocui.ErrUnknownView) {
|
||||
return err
|
||||
}
|
||||
help.SelBgColor = gocui.ColorGreen
|
||||
help.SelFgColor = gocui.ColorBlack
|
||||
// fmt.Fprintln(help, "Enter: Click Button")
|
||||
// fmt.Fprintln(help, "Tab/Space: Switch Buttons")
|
||||
// fmt.Fprintln(help, "Backspace: Delete Button")
|
||||
// fmt.Fprintln(help, "Arrow keys: Move Button")
|
||||
|
||||
fmt.Fprintln(help, strings.Join(helpText, "\n"))
|
||||
|
||||
|
@ -66,6 +88,21 @@ func helplayout() error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
g.SetViewOnTop("help")
|
||||
me.helpLabel = help
|
||||
|
||||
/*
|
||||
if me.treeRoot == nil {
|
||||
log.Info("gogui makeClock() error. treeRoot == nil")
|
||||
return nil
|
||||
} else {
|
||||
if me.stdout.tk == nil {
|
||||
makeOutputWidget(me.baseGui, "made this in showHelp()")
|
||||
msg := fmt.Sprintf("test to stdout from in showHelp() %d\n", me.ecount)
|
||||
me.stdout.Write([]byte(msg))
|
||||
log.Log(NOW, "log.log(NOW) test")
|
||||
}
|
||||
}
|
||||
*/
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,464 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
// Copyright 2014 The gocui Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"runtime/pprof"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/toolkits/tree"
|
||||
)
|
||||
|
||||
// sent via -ldflags
|
||||
var VERSION string
|
||||
var BUILDTIME string
|
||||
|
||||
var PLUGIN string = "gocui"
|
||||
|
||||
// this is called at the very initial connection
|
||||
// between the app and this gocui plugin
|
||||
// this is a good place to initialize gocui's default behavior
|
||||
func toolkitInit() {
|
||||
log.Log(INFO, "gocui toolkitInit() me.ok =", me.ok)
|
||||
if me.baseGui == nil {
|
||||
log.Info("gocui baseGui is still nil")
|
||||
standardExit()
|
||||
}
|
||||
if me.treeRoot == nil {
|
||||
log.Info("gocui treeRoot is still nil")
|
||||
standardExit()
|
||||
}
|
||||
// w := me.treeRoot.TK.(*guiWidget)
|
||||
// w.dumpTree("MM")
|
||||
// w.dumpWindows("WW")
|
||||
|
||||
// SETUP HELP START
|
||||
me.baseGui.Update(testRefresh)
|
||||
log.Log(INFO, "gocui toolkitInit() trying showHelp() me.ok =", me.ok)
|
||||
showHelp()
|
||||
hideHelp()
|
||||
// SETUP HELP END
|
||||
|
||||
// SETUP STDOUT START
|
||||
if me.stdout.tk == nil {
|
||||
makeOutputWidget(me.baseGui, "from setThingsOnTop()")
|
||||
}
|
||||
|
||||
// time.Sleep(300 * time.Millisecond)
|
||||
log.Log(INFO, "gocui toolkitInit() me.ok =", me.ok)
|
||||
me.baseGui.Update(testRefresh)
|
||||
if !me.stdout.init {
|
||||
log.Log(INFO, "gocui toolkitInit() stdout.Init me.ok =", me.ok)
|
||||
me.stdout.init = true
|
||||
relocateStdoutOffscreen()
|
||||
}
|
||||
// time.Sleep(1 * time.Second)
|
||||
me.stdout.outputOnTop = false
|
||||
setThingsOnTop()
|
||||
// SETUP STDOUT END
|
||||
|
||||
// SETUP BG
|
||||
if me.BG.tk == nil {
|
||||
me.BG.tk = makeNewInternalWidget(me.BG.wId)
|
||||
}
|
||||
|
||||
// SETUP libnotify clock and menu
|
||||
me.notify.clock.once.Do(makeNotifyClock)
|
||||
me.notify.icon.once.Do(makeNotifyIcon)
|
||||
|
||||
// TODO: for some reason, this makes the background doesn't display
|
||||
// PUT INIT DEBUG COOE HERE
|
||||
var toggle bool
|
||||
for i := 0; i < 4; i++ {
|
||||
// enable this to show early debugging
|
||||
// w := me.treeRoot.TK.(*guiWidget)
|
||||
// w.dumpTree("MM")
|
||||
// w.dumpWindows("WW")
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
if toggle {
|
||||
toggle = false
|
||||
// log.Info("gocui toolkitInit() put testing true stuff here")
|
||||
} else {
|
||||
toggle = true
|
||||
// log.Info("gocui toolkitInit() put testing false stuff here")
|
||||
}
|
||||
setBottomBG()
|
||||
}
|
||||
// PUT INIT DEBUG COOE HERE END
|
||||
|
||||
// TEST TEXTBOX START
|
||||
// time.Sleep(1 * time.Second)
|
||||
log.Log(INFO, "gocui toolkitInit() me.ok =", me.ok)
|
||||
me.baseGui.Update(testRefresh)
|
||||
if me.textbox.tk == nil {
|
||||
log.Log(INFO, "gocui toolkitInit() initTextbox me.ok =", me.ok)
|
||||
initTextbox()
|
||||
}
|
||||
// TEST TEXTBOX END
|
||||
}
|
||||
|
||||
func toolkitClose() {
|
||||
me.baseGui.Close()
|
||||
}
|
||||
|
||||
// a GO GUI plugin should initTree in init()
|
||||
// this should be done before the application
|
||||
// starts trying to open up a channel
|
||||
func init() {
|
||||
me.myTree = initTree()
|
||||
|
||||
}
|
||||
|
||||
// sets defaults and establishes communication
|
||||
// to this toolkit from the wit/gui golang package
|
||||
func initPlugin() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Fprintf(me.outf, "PANIC: initPlugin() recovered %v\n", r)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
var err error
|
||||
|
||||
// read in defaults from config protobuf
|
||||
if val, err := me.myTree.ConfigFind("stdout"); err == nil {
|
||||
if val == "true" {
|
||||
me.stdout.startOnscreen = true
|
||||
// me.stdout.Write([]byte("starting with stdout onscreen\n"))
|
||||
}
|
||||
if val == "disable" {
|
||||
log.Log(INFO, "gocui: attempt to COMPLETELY DISABLE STDOUT LOG")
|
||||
me.stdout.disable = true
|
||||
}
|
||||
}
|
||||
if val, err := me.myTree.ConfigFind("stdoutoffscreen"); err == nil {
|
||||
if val == "false" {
|
||||
// log.Log(NOW, "gocui: START ON SCREEN TRUE")
|
||||
me.stdout.startOnscreen = true
|
||||
}
|
||||
}
|
||||
if val, err := me.myTree.ConfigFind("dark"); err == nil {
|
||||
if val == "true" {
|
||||
me.dark = true
|
||||
}
|
||||
} else {
|
||||
// macos iterm2 really only works with dark mode right now
|
||||
if runtime.GOOS == "macos" {
|
||||
me.dark = true
|
||||
}
|
||||
}
|
||||
// todo: make this a tmp file that goes away
|
||||
if !me.stdout.disable {
|
||||
tmpFile, err := os.CreateTemp("", "gocui-*.log")
|
||||
if err != nil {
|
||||
fmt.Println("Error creating temp file:", err)
|
||||
standardExit()
|
||||
}
|
||||
// defer os.Remove(tmpFile.Name())
|
||||
|
||||
log.Log(INFO, "stdout.disable == true. writing to", tmpFile.Name())
|
||||
me.outf = tmpFile
|
||||
// todo: some early output still goes to the /tmp/ file
|
||||
//time.Sleep(200 * time.Millisecond)
|
||||
log.CaptureMode(me.stdout)
|
||||
}
|
||||
me.starttime = time.Now()
|
||||
log.Log(INFO, "Init() of awesome-gocui")
|
||||
|
||||
// init the config struct default values
|
||||
Set(&me, "default")
|
||||
|
||||
// initial app window settings
|
||||
|
||||
// initial stdout window settings
|
||||
me.stdout.w = 180
|
||||
me.stdout.h = 40
|
||||
me.stdout.lastW = 4
|
||||
me.stdout.lastH = 20
|
||||
|
||||
if val, err := me.myTree.ConfigFind("stdoutsize"); err == nil {
|
||||
parts := strings.Fields(val)
|
||||
if len(parts) == 4 {
|
||||
log.Info("initial stdout settings:", parts, "setting startOnscreen = true")
|
||||
me.stdout.w, _ = strconv.Atoi(parts[0])
|
||||
me.stdout.h, _ = strconv.Atoi(parts[1])
|
||||
me.stdout.lastW, _ = strconv.Atoi(parts[2])
|
||||
me.stdout.lastH, _ = strconv.Atoi(parts[3])
|
||||
me.stdout.startOnscreen = true
|
||||
} else {
|
||||
log.Info("initial stdout settings parse error:", parts)
|
||||
}
|
||||
}
|
||||
|
||||
// just make up unique values for these
|
||||
me.dropdown.wId = -77
|
||||
me.textbox.wId = -55
|
||||
me.stdout.wId = -4
|
||||
me.BG.wId = -22
|
||||
|
||||
// the clock widget id and offset
|
||||
me.notify.clock.wId = -5
|
||||
me.notify.clock.offsetW = 13
|
||||
me.notify.clock.offsetH = 1
|
||||
|
||||
me.notify.icon.wId = -6
|
||||
me.notify.icon.offsetW = 4
|
||||
me.notify.icon.offsetH = 1
|
||||
|
||||
me.notify.help.wId = -7
|
||||
me.notify.help.offsetH = 3
|
||||
|
||||
Set(&me.dropdown, "default")
|
||||
// s := fmt.Sprintln("fake default check =", me.FakeW, "dropdown.Id", me.dropdown.Id)
|
||||
// me.stdout.Write([]byte(s))
|
||||
|
||||
me.mouse.mouseUp = true
|
||||
me.mouse.clicktime = time.Millisecond * 200
|
||||
me.mouse.doubletime = time.Millisecond * 400
|
||||
|
||||
me.newWindowTrigger = make(chan *guiWidget, 1)
|
||||
go newWindowTrigger()
|
||||
go refreshGocui()
|
||||
|
||||
log.Log(NOW, "Init() start pluginChan")
|
||||
if me.stdout.disable {
|
||||
log.Info("Using STDOUT")
|
||||
} else {
|
||||
log.Info("Using gocui STDOUT")
|
||||
os.Stdout = me.outf
|
||||
log.CaptureMode(me.outf)
|
||||
}
|
||||
|
||||
// init gocui
|
||||
g, err := gocui.NewGui(gocui.OutputNormal, true)
|
||||
if err != nil {
|
||||
os.Exit(-1)
|
||||
return
|
||||
}
|
||||
me.baseGui = g
|
||||
g.Cursor = true
|
||||
g.Mouse = true
|
||||
|
||||
// this sets the function that is run on every event. For example:
|
||||
// When you click the mouse, move the mouse, or press a key on the keyboard
|
||||
// This is equivalent to xev or similar to cat /dev/input on linux
|
||||
g.SetManagerFunc(gocuiEvent)
|
||||
|
||||
// register how the 'gocui' will work as a GO toolkit plugin
|
||||
// all applications will use these keys. they are universal.
|
||||
// registered event handlers still have the events sent to gocuiEvent() above
|
||||
registerHandlers(g)
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
if me.outf != nil {
|
||||
fmt.Fprintln(me.outf, "hello world", time.Since(me.starttime))
|
||||
}
|
||||
|
||||
// coreStdout()
|
||||
// createStdout(g)
|
||||
// tell 'tree' that we are okay to start talking to
|
||||
me.myTree.InitOK()
|
||||
me.ok = true // this tells init() it's okay to work with gocui
|
||||
|
||||
go gocuiMain()
|
||||
}
|
||||
|
||||
// This goroutine sits in gocui's MainLoop()
|
||||
func gocuiMain() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Warn("PANIC ecovered in gocuiMain()", r)
|
||||
if me.outf == nil {
|
||||
debug.PrintStack()
|
||||
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
|
||||
panic(os.Stdout)
|
||||
} else {
|
||||
fmt.Fprintf(me.outf, "PANIC recovered in r = %v", r)
|
||||
os.Stderr = me.outf
|
||||
os.Stdout = me.outf
|
||||
debug.PrintStack()
|
||||
pprof.Lookup("goroutine").WriteTo(me.outf, 1)
|
||||
panic(me.outf)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// me.stdout.Write([]byte("begin gogui.MainLoop()\n"))
|
||||
if err := me.baseGui.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) {
|
||||
log.Log(NOW, "g.MainLoop() panic err =", err)
|
||||
// normally panic here
|
||||
panic("gocuiTKmainloop OOPS")
|
||||
}
|
||||
}
|
||||
|
||||
func standardExit() {
|
||||
log.Log(NOW, "standardExit() doing baseGui.Close()")
|
||||
me.baseGui.Close()
|
||||
if me.outf != nil {
|
||||
log.Log(NOW, "standardExit() doing outf.Close()")
|
||||
me.outf.Close()
|
||||
os.Remove(me.outf.Name())
|
||||
}
|
||||
// log(true, "standardExit() setOutput(os.Stdout)")
|
||||
// setOutput(os.Stdout)
|
||||
log.Log(NOW, "standardExit() send back Quit()")
|
||||
// go sendBackQuit() // don't stall here in case the
|
||||
// induces a delay in case the callback channel is broken
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
log.Log(NOW, "standardExit() exit()")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func standardClose() {
|
||||
log.Log(NOW, "standardExit() doing baseGui.Close()")
|
||||
me.baseGui.Close()
|
||||
log.Log(NOW, "standardExit() doing outf.Close()")
|
||||
me.outf.Close()
|
||||
os.Remove(me.outf.Name())
|
||||
// os.Stdin = os.Stdin
|
||||
// os.Stdout = os.Stdout
|
||||
// os.Stderr = os.Stderr
|
||||
log.Log(NOW, "standardExit() send back Quit()")
|
||||
}
|
||||
|
||||
func main() {
|
||||
}
|
||||
|
||||
// this hack is to wait for the application to send something
|
||||
// before trying to do anything. todo: rethink this someday
|
||||
func waitOK() {
|
||||
for {
|
||||
if me.ok {
|
||||
return
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// this hack is to wait for the application to send something
|
||||
// before trying to do anything. todo: rethink this someday
|
||||
func waitFirstWindow() {
|
||||
for {
|
||||
if me.firstWindowOk {
|
||||
return
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// empty function. this triggers gocui to refresh the screen
|
||||
func testRefresh(*gocui.Gui) error {
|
||||
// log.Info("in testRefresh")
|
||||
return nil
|
||||
}
|
||||
|
||||
// refresh the screen 10 times a second
|
||||
func refreshGocui() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if me.outf == nil {
|
||||
log.Info("INIT PLUGIN recovered in r", r)
|
||||
} else {
|
||||
fmt.Fprintln(me.outf, "INIT PLUGIN recovered in r", r)
|
||||
}
|
||||
return
|
||||
}
|
||||
}()
|
||||
var lastRefresh time.Time
|
||||
lastRefresh = time.Now()
|
||||
me.refresh = false
|
||||
for {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
// log.Info("refresh checking ok")
|
||||
if !me.ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// redraw the windows if something has changed
|
||||
if time.Since(lastRefresh) > 1000*time.Millisecond {
|
||||
if me.refresh {
|
||||
log.Log(NOW, "newWindowTrigger() sending refresh to channel")
|
||||
me.newWindowTrigger <- me.treeRoot.TK.(*guiWidget)
|
||||
me.refresh = false
|
||||
}
|
||||
if me.stdout.changed {
|
||||
me.stdout.changed = false
|
||||
lastRefresh = time.Now()
|
||||
new1 := new(tree.ToolkitConfig)
|
||||
new1.Plugin = "gocui"
|
||||
new1.Name = "stdoutsize"
|
||||
width := me.stdout.tk.gocuiSize.w1 - me.stdout.tk.gocuiSize.w0
|
||||
height := me.stdout.tk.gocuiSize.h1 - me.stdout.tk.gocuiSize.h0
|
||||
new1.Value = fmt.Sprintf("%d %d %d %d", width, height, me.stdout.tk.gocuiSize.w0, me.stdout.tk.gocuiSize.h0)
|
||||
me.myTree.ConfigSave(new1)
|
||||
// log.Log(NOW, "newWindowTrigger() gocui setting stdout size =", new1.Value)
|
||||
// me.stdout.tk.dumpWidget("save")
|
||||
}
|
||||
}
|
||||
|
||||
// this code updates the clock
|
||||
if time.Since(lastRefresh) > 1000*time.Millisecond {
|
||||
// artificially pause clock while dragging.
|
||||
// this is a reminder to make this refresh code smarter
|
||||
// after the switch to protocol buffers
|
||||
me.myTree.Lock()
|
||||
if me.mouse.mouseUp {
|
||||
// log.Info("refresh now on mouseUp")
|
||||
// todo: add logic here to see if the application has changed anything
|
||||
libNotifyUpdate()
|
||||
lastRefresh = time.Now()
|
||||
} else {
|
||||
if time.Since(lastRefresh) > 3*time.Second {
|
||||
libNotifyUpdate()
|
||||
lastRefresh = time.Now()
|
||||
}
|
||||
}
|
||||
me.myTree.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set the widget start width & height
|
||||
|
||||
func newWindowTrigger() {
|
||||
// log.Log(NOW, "newWindowTriggerl() START")
|
||||
for {
|
||||
// log.Log(NOW, "GO plugin toolkit made a new window")
|
||||
select {
|
||||
case tk := <-me.newWindowTrigger:
|
||||
// log.Log(NOW, "newWindowTrigger() got new window", tk.cuiName)
|
||||
// time.Sleep(200 * time.Millisecond)
|
||||
waitOK()
|
||||
me.myTree.Lock()
|
||||
// time.Sleep(200 * time.Millisecond)
|
||||
redoWindows(me.FirstWindowW, me.FirstWindowH)
|
||||
me.firstWindowOk = true
|
||||
if !me.stdout.init {
|
||||
me.stdout.init = true
|
||||
relocateStdoutOffscreen()
|
||||
}
|
||||
if me.textbox.tk == nil {
|
||||
initTextbox()
|
||||
}
|
||||
tk.makeWindowActive()
|
||||
me.myTree.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
165
keybindings.go
165
keybindings.go
|
@ -1,165 +0,0 @@
|
|||
// Copyright 2014 The gocui Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/awesome-gocui/gocui"
|
||||
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func defaultKeybindings(g *gocui.Gui) error {
|
||||
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, n := range []string{"but1", "but2", "help", "but3"} {
|
||||
if err := g.SetKeybinding(n, gocui.MouseLeft, gocui.ModNone, showMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := g.SetKeybinding("", gocui.MouseRelease, gocui.ModNone, mouseUp); err != nil {
|
||||
return err
|
||||
}
|
||||
// mouseDown() runs whenever you click on an unknown view (?)
|
||||
if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, mouseDown); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModMouseCtrl, ctrlDown); err != nil {
|
||||
return err
|
||||
}
|
||||
// if err := g.SetKeybinding(w.v.Name(), gocui.MouseLeft, gocui.ModNone, click); err != nil {
|
||||
// return err
|
||||
// }
|
||||
/*
|
||||
if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, globalDown); err != nil {
|
||||
return err
|
||||
}
|
||||
*/
|
||||
if err := g.SetKeybinding("msg", gocui.MouseLeft, gocui.ModNone, msgDown); err != nil {
|
||||
return err
|
||||
}
|
||||
addDebugKeys(g)
|
||||
return nil
|
||||
}
|
||||
|
||||
func addDebugKeys(g *gocui.Gui) {
|
||||
// show debugging buttons
|
||||
g.SetKeybinding("", 'd', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
fakeStartWidth = me.FakeW
|
||||
fakeStartHeight = me.TabH + me.FramePadH
|
||||
if showDebug {
|
||||
me.rootNode.showFake()
|
||||
showDebug = false
|
||||
} else {
|
||||
me.rootNode.hideFake()
|
||||
showDebug = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// display the help menu
|
||||
g.SetKeybinding("", '?', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
if showHelp {
|
||||
helplayout()
|
||||
showHelp = false
|
||||
} else {
|
||||
me.baseGui.DeleteView("help")
|
||||
showHelp = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// redraw all the widgets
|
||||
g.SetKeybinding("", 'r', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
if redoWidgets {
|
||||
redoWindows(0, 0)
|
||||
redoWidgets = false
|
||||
} else {
|
||||
me.rootNode.hideWidgets()
|
||||
redoWidgets = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// hide all widgets
|
||||
g.SetKeybinding("", 'h', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
me.rootNode.hideWidgets()
|
||||
return nil
|
||||
})
|
||||
|
||||
// show all widgets
|
||||
g.SetKeybinding("", 's', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
me.rootNode.showWidgets()
|
||||
return nil
|
||||
})
|
||||
|
||||
// list all widgets
|
||||
g.SetKeybinding("", 'L', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
me.rootNode.listWidgets()
|
||||
return nil
|
||||
})
|
||||
|
||||
// list all widgets with positions
|
||||
g.SetKeybinding("", 'M', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
me.rootNode.dumpTree(true)
|
||||
return nil
|
||||
})
|
||||
|
||||
// log to output window
|
||||
g.SetKeybinding("", 'o', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
log.Log(ERROR, "TODO: re-implement this")
|
||||
if me.logStdout.Visible() {
|
||||
me.logStdout.SetVisible(false)
|
||||
// setOutput(os.Stdout)
|
||||
} else {
|
||||
me.logStdout.SetVisible(true)
|
||||
// setOutput(me.logStdout.tk)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// exit
|
||||
g.SetKeybinding("", 'q', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
standardExit()
|
||||
return nil
|
||||
})
|
||||
g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
standardExit()
|
||||
return nil
|
||||
})
|
||||
g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
if showDebug {
|
||||
var a widget.Action
|
||||
a.Value = true
|
||||
a.ActionType = widget.EnableDebug
|
||||
callback <- a
|
||||
}
|
||||
return nil
|
||||
})
|
||||
g.SetKeybinding("", gocui.KeyCtrlV, gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
// panic
|
||||
g.SetKeybinding("", 'p', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
standardExit()
|
||||
panic("forced panic in gocui")
|
||||
return nil
|
||||
})
|
||||
}
|
|
@ -0,0 +1,295 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
// this file implements a libnotify-like menu
|
||||
// also there is SIGWINCH resizing
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
log "go.wit.com/log"
|
||||
"go.wit.com/toolkits/tree"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
// create a new widget in the binary tree
|
||||
func makeNewInternalWidget(wId int) *guiWidget {
|
||||
if me.treeRoot == nil {
|
||||
log.Info("GOGUI Init ERROR. treeRoot == nil")
|
||||
return nil
|
||||
}
|
||||
n := new(tree.Node)
|
||||
n.WidgetType = widget.Flag
|
||||
n.WidgetId = wId
|
||||
n.ParentId = 0
|
||||
|
||||
// store the internal toolkit information
|
||||
tk := new(guiWidget)
|
||||
tk.frame = true
|
||||
|
||||
tk.node = n
|
||||
tk.node.Parent = me.treeRoot
|
||||
|
||||
// set the name used by gocui to the id
|
||||
tk.cuiName = fmt.Sprintf("%d DR", wId)
|
||||
|
||||
tk.setColorInput()
|
||||
|
||||
// add this new widget on the binary tree
|
||||
tk.parent = me.treeRoot.TK.(*guiWidget)
|
||||
if tk.parent == nil {
|
||||
panic("makeNewFlagWidget() didn't get treeRoot guiWidget")
|
||||
} else {
|
||||
tk.parent.children = append(tk.parent.children, tk)
|
||||
}
|
||||
|
||||
n.TK = tk
|
||||
return tk
|
||||
}
|
||||
|
||||
func makeNotifyClock() {
|
||||
if me.treeRoot == nil {
|
||||
log.Info("gogui makeClock() error. treeRoot == nil")
|
||||
return
|
||||
}
|
||||
me.notify.clock.tk = makeNewInternalWidget(me.notify.clock.wId)
|
||||
// me.notify.clock.tk.dumpWidget("init() clock")
|
||||
me.notify.clock.tk.MoveToOffset(0, 0)
|
||||
me.notify.clock.tk.labelN = time.Now().Format("15:04:05")
|
||||
me.notify.clock.tk.frame = false
|
||||
me.notify.clock.tk.setColorLabel()
|
||||
me.notify.clock.tk.Show()
|
||||
me.notify.clock.active = true
|
||||
// me.notify.clock.tk.dumpWidget("notifyClock()")
|
||||
|
||||
}
|
||||
|
||||
func makeNotifyIcon() {
|
||||
if me.treeRoot == nil {
|
||||
log.Info("gogui makeClock() error. treeRoot == nil")
|
||||
return
|
||||
}
|
||||
me.notify.icon.tk = makeNewInternalWidget(me.notify.icon.wId)
|
||||
// me.notify.icon.tk.dumpWidget("init() menu")
|
||||
w, _ := me.baseGui.Size()
|
||||
me.notify.icon.tk.MoveToOffset(w-5, me.notify.icon.offsetH)
|
||||
me.notify.icon.tk.labelN = "[ ]"
|
||||
me.notify.icon.tk.frame = false
|
||||
me.notify.icon.tk.setColorNotifyIcon()
|
||||
me.notify.icon.tk.Show()
|
||||
me.notify.icon.active = true
|
||||
// me.notify.icon.tk.dumpWidget("notifyIcon()")
|
||||
|
||||
}
|
||||
|
||||
func libNotifyUpdate() {
|
||||
if me.baseGui == nil {
|
||||
log.Info("libNotifyUpdate error baseGui == nil")
|
||||
return
|
||||
}
|
||||
|
||||
// refresh GOCUI
|
||||
me.baseGui.Update(testRefresh)
|
||||
// me.baseGui.UpdateAsync(testRefresh) // Async option. probably don't need this?
|
||||
|
||||
if me.notify.clock.tk == nil {
|
||||
log.Info("libNotifyUpdate error clock.tk == nil")
|
||||
return
|
||||
}
|
||||
|
||||
// check for SIGWINCH. If so, move the libnotify clock
|
||||
w, h := me.baseGui.Size()
|
||||
if me.winchW != w || me.winchH != h {
|
||||
if me.winchW == 0 && me.winchH == 0 {
|
||||
// this isn't really SIGWINCH. This is the app starting
|
||||
} else {
|
||||
log.Printf("gocui: long live SIGWINCH! (w,h) is now (%d,%d)\n", w, h)
|
||||
}
|
||||
me.winchW = w
|
||||
me.winchH = h
|
||||
me.notify.clock.tk.MoveToOffset(w-me.notify.clock.offsetW, me.notify.clock.offsetH)
|
||||
me.notify.clock.tk.Hide()
|
||||
me.notify.clock.tk.Show()
|
||||
|
||||
sigWinchBG()
|
||||
sigWinchIcon()
|
||||
}
|
||||
|
||||
// update the time
|
||||
me.notify.clock.tk.v.Clear()
|
||||
me.notify.clock.tk.labelN = time.Now().Format("15:04:05")
|
||||
me.notify.clock.tk.v.WriteString(me.notify.clock.tk.labelN)
|
||||
hardDrawAtgocuiSize(me.notify.clock.tk)
|
||||
// hardDrawUnderMouse(me.notify.clock.tk, "clock")
|
||||
// log.Info("libNotifyUpdate updated clock", me.notify.clock.tk.labelN)
|
||||
|
||||
if me.notify.icon.tk == nil {
|
||||
log.Info("libNotifyUpdate error menu.tk == nil")
|
||||
return
|
||||
}
|
||||
if me.notify.icon.tk.v == nil {
|
||||
log.Info("libNotifyUpdate error menu.tk.v == nil")
|
||||
return
|
||||
}
|
||||
|
||||
// update the menu
|
||||
hardDrawAtgocuiSize(me.notify.icon.tk)
|
||||
me.notify.icon.tk.setColorNotifyIcon()
|
||||
me.baseGui.SetViewOnTop(me.notify.icon.tk.v.Name())
|
||||
me.baseGui.SetViewOnTop(me.notify.clock.tk.v.Name())
|
||||
}
|
||||
|
||||
func setNotifyIconText(s string) {
|
||||
me.notify.icon.tk.v.Clear()
|
||||
me.notify.icon.tk.labelN = s
|
||||
me.notify.icon.tk.v.WriteString(me.notify.icon.tk.labelN)
|
||||
hardDrawAtgocuiSize(me.notify.icon.tk)
|
||||
me.notify.icon.tk.setColorNotifyIcon()
|
||||
me.baseGui.SetViewOnTop(me.notify.icon.tk.v.Name())
|
||||
log.Info("setNotifyIconText() updated menu to:", me.notify.icon.tk.labelN)
|
||||
// print out the window list // TODO: put this in libnotify
|
||||
for _, tk := range me.allwin {
|
||||
log.Info("known window Window", tk.labelN, tk.window.active, tk.window.order)
|
||||
}
|
||||
if s == "[X]" {
|
||||
log.Warn("should turn on help window here")
|
||||
showHelp()
|
||||
} else {
|
||||
log.Warn("should turn off help window here")
|
||||
hideHelp()
|
||||
}
|
||||
}
|
||||
|
||||
// in the very end of redrawing things, this will place the help and stdout on the top or botton
|
||||
// depending on the state the user has chosen
|
||||
func setThingsOnTop() {
|
||||
if me.showHelp { // terrible variable name. FIXME
|
||||
// log.Info("help does not exist")
|
||||
} else {
|
||||
me.baseGui.SetViewOnTop("help")
|
||||
}
|
||||
|
||||
if me.notify.clock.tk != nil {
|
||||
me.baseGui.SetViewOnTop(me.notify.clock.tk.v.Name())
|
||||
}
|
||||
|
||||
if me.notify.icon.tk != nil {
|
||||
if me.notify.icon.tk.v != nil {
|
||||
me.baseGui.SetViewOnTop(me.notify.icon.tk.v.Name())
|
||||
}
|
||||
}
|
||||
|
||||
if me.stdout.tk == nil {
|
||||
makeOutputWidget(me.baseGui, "from setThingsOnTop()")
|
||||
}
|
||||
if me.stdout.tk == nil {
|
||||
return
|
||||
}
|
||||
if me.stdout.tk.v == nil {
|
||||
return
|
||||
}
|
||||
if me.dark {
|
||||
me.stdout.tk.v.FgColor = gocui.ColorWhite
|
||||
me.stdout.tk.v.BgColor = gocui.ColorBlack
|
||||
} else {
|
||||
me.stdout.tk.v.FgColor = gocui.ColorBlack
|
||||
me.stdout.tk.v.BgColor = gocui.AttrNone
|
||||
}
|
||||
|
||||
if me.stdout.outputOnTop {
|
||||
me.baseGui.SetViewOnTop("msg")
|
||||
} else {
|
||||
me.baseGui.SetViewOnBottom("msg")
|
||||
}
|
||||
if me.stdout.startOnscreen {
|
||||
// log.Info("THIS TRIGGERS STDOUT") // todo: make a proper init() & move this there
|
||||
me.stdout.tk.relocateStdout(me.stdout.lastW, me.stdout.lastH)
|
||||
me.stdout.startOnscreen = false
|
||||
}
|
||||
setBottomBG()
|
||||
}
|
||||
|
||||
// useful for debuggging
|
||||
func hardDrawUnderMouse(tk *guiWidget, name string) {
|
||||
if tk.v != nil {
|
||||
tk.Hide()
|
||||
}
|
||||
w, h := me.baseGui.MousePosition()
|
||||
r := new(rectType)
|
||||
r.w0 = w
|
||||
r.h0 = h
|
||||
r.w1 = w + 8
|
||||
r.h1 = h + 4
|
||||
if err := tk.SetViewRect(r); err != nil {
|
||||
log.Info("hardDrawUnderMouse() err", tk.cuiName, err)
|
||||
tk.dumpWidget("hardDrawERR")
|
||||
}
|
||||
tk.v.Frame = false
|
||||
tk.v.Clear()
|
||||
tk.v.WriteString(tk.labelN + "\n" + name)
|
||||
}
|
||||
|
||||
func hardDrawAtgocuiSize(tk *guiWidget) {
|
||||
if tk.v != nil {
|
||||
tk.Hide()
|
||||
}
|
||||
if err := tk.SetView(); err != nil {
|
||||
log.Info("hardDrawAtgocuiSize() err ok widget", tk.cuiName)
|
||||
tk.dumpWidget("hardDrawERR")
|
||||
}
|
||||
tk.v.Frame = false
|
||||
tk.v.Clear()
|
||||
tk.v.WriteString(tk.labelN)
|
||||
// log.Verbose("hardDrawAtgocuiSize() err ok widget", tk.cuiName, a, b, c, d, tk.v.Name())
|
||||
}
|
||||
|
||||
func sigWinchIcon() {
|
||||
w, _ := me.baseGui.Size()
|
||||
me.notify.icon.tk.MoveToOffset(w-me.notify.icon.offsetW, me.notify.icon.offsetH)
|
||||
me.notify.icon.tk.Hide()
|
||||
me.notify.icon.tk.Show()
|
||||
}
|
||||
|
||||
func sigWinchBG() {
|
||||
tk := me.BG.tk
|
||||
w, h := me.baseGui.Size()
|
||||
tk.gocuiSize.w0 = -1
|
||||
tk.gocuiSize.h0 = -1
|
||||
tk.gocuiSize.w1 = w + 1
|
||||
tk.gocuiSize.h1 = h + 1
|
||||
if err := tk.SetView(); err != nil {
|
||||
tk.dumpWidget("sigWinchBGerr()")
|
||||
log.Log(ERROR, "sigWinchBG()", err)
|
||||
}
|
||||
log.Log(INFO, "background resized to", tk.gocuiSize)
|
||||
}
|
||||
|
||||
// find the "BG" widget and set it to the background on the very very bottom
|
||||
func setBottomBG() {
|
||||
if me.BG.tk == nil {
|
||||
log.Info("background tk widget not initialized")
|
||||
return
|
||||
}
|
||||
tk := me.BG.tk
|
||||
// log.Info("found BG. setting to bottom", tk.cuiName)
|
||||
if tk.v == nil {
|
||||
sigWinchBG()
|
||||
return
|
||||
}
|
||||
if me.dark {
|
||||
tk.v.BgColor = gocui.ColorBlack
|
||||
} else {
|
||||
tk.v.BgColor = gocui.ColorWhite
|
||||
}
|
||||
tk.v.Clear()
|
||||
me.baseGui.SetViewOnBottom(tk.cuiName)
|
||||
w, h := me.baseGui.Size()
|
||||
tk.gocuiSize.w0 = -1
|
||||
tk.gocuiSize.h0 = -1
|
||||
tk.gocuiSize.w1 = w + 1
|
||||
tk.gocuiSize.h1 = h + 1
|
||||
tk.SetView()
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
/*
|
||||
|
@ -8,10 +11,9 @@ import (
|
|||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
var outputS []string
|
||||
|
||||
var NOW *log.LogFlag
|
||||
var INFO *log.LogFlag
|
||||
var GOCUI *log.LogFlag
|
||||
|
||||
var SPEW *log.LogFlag
|
||||
var WARN *log.LogFlag
|
||||
|
@ -19,8 +21,13 @@ var WARN *log.LogFlag
|
|||
var ERROR *log.LogFlag
|
||||
|
||||
func init() {
|
||||
full := "toolkit/nocui"
|
||||
short := "nocui"
|
||||
full := "go.wit.com/gui"
|
||||
short := "gocui"
|
||||
|
||||
GOCUI = log.NewFlag("GOCUI", true, full, short, "gocui internals")
|
||||
|
||||
full = "go.wit.com/toolkits/gocui"
|
||||
short = "gocui"
|
||||
|
||||
NOW = log.NewFlag("NOW", true, full, short, "temp debugging stuff")
|
||||
INFO = log.NewFlag("INFO", false, full, short, "normal debugging stuff")
|
||||
|
@ -28,5 +35,5 @@ func init() {
|
|||
WARN = log.NewFlag("WARN", true, full, short, "bad things")
|
||||
SPEW = log.NewFlag("SPEW", false, full, short, "spew stuff")
|
||||
|
||||
ERROR = log.NewFlag("ERROR", false, full, short, "toolkit errors")
|
||||
ERROR = log.NewFlag("ERROR", true, full, short, "toolkit errors")
|
||||
}
|
104
main.go
104
main.go
|
@ -1,104 +0,0 @@
|
|||
// Copyright 2014 The gocui Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
// sets defaults and establishes communication
|
||||
// to this toolkit from the wit/gui golang package
|
||||
func init() {
|
||||
log.Log(INFO, "Init() of awesome-gocui")
|
||||
|
||||
// init the config struct default values
|
||||
Set(&me, "default")
|
||||
|
||||
pluginChan = make(chan widget.Action)
|
||||
|
||||
log.Log(NOW, "Init() start pluginChan")
|
||||
go catchActionChannel()
|
||||
log.Sleep(.1) // probably not needed, but in here for now under development
|
||||
go main()
|
||||
log.Sleep(.1) // probably not needed, but in here for now under development
|
||||
}
|
||||
|
||||
/*
|
||||
recieves requests from the program to do things like:
|
||||
* add new widgets
|
||||
* change the text of a label
|
||||
* etc..
|
||||
*/
|
||||
func catchActionChannel() {
|
||||
log.Log(INFO, "catchActionChannel() START")
|
||||
for {
|
||||
log.Log(INFO, "catchActionChannel() infinite for() loop restarted select on channel")
|
||||
select {
|
||||
case a := <-pluginChan:
|
||||
if me.baseGui == nil {
|
||||
// something went wrong initializing the gocui
|
||||
log.Log(ERROR, "ERROR: console did not initialize")
|
||||
continue
|
||||
}
|
||||
log.Log(INFO, "catchActionChannel()", a.WidgetId, a.ActionType, a.WidgetType, a.ProgName)
|
||||
action(&a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Exit() {
|
||||
// TODO: what should actually happen here?
|
||||
log.Log(NOW, "Exit() here. doing standardExit()")
|
||||
standardExit()
|
||||
}
|
||||
|
||||
func standardExit() {
|
||||
log.Log(NOW, "standardExit() doing baseGui.Close()")
|
||||
me.baseGui.Close()
|
||||
log.Log(NOW, "standardExit() doing outf.Close()")
|
||||
outf.Close()
|
||||
// log(true, "standardExit() setOutput(os.Stdout)")
|
||||
// setOutput(os.Stdout)
|
||||
log.Log(NOW, "standardExit() send back Quit()")
|
||||
go sendBackQuit() // don't stall here in case the
|
||||
// induces a delay in case the callback channel is broken
|
||||
log.Sleep(1)
|
||||
log.Log(NOW, "standardExit() exit()")
|
||||
os.Exit(0)
|
||||
}
|
||||
func sendBackQuit() {
|
||||
// send 'Quit' back to the program (?)
|
||||
var a widget.Action
|
||||
a.ActionType = widget.UserQuit
|
||||
callback <- a
|
||||
}
|
||||
|
||||
var outf *os.File
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
log.Log(INFO, "main() start Init()")
|
||||
|
||||
outf, err = os.OpenFile("/tmp/witgui.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
log.Error(err, "error opening file: %v")
|
||||
os.Exit(0)
|
||||
}
|
||||
os.Stdout = outf
|
||||
defer outf.Close()
|
||||
|
||||
// setOutput(outf)
|
||||
// log("This is a test log entry")
|
||||
|
||||
ferr, _ := os.OpenFile("/tmp/witgui.err", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664)
|
||||
os.Stderr = ferr
|
||||
gocuiMain()
|
||||
|
||||
log.Log(NOW, "MouseMain() closed")
|
||||
standardExit()
|
||||
}
|
151
mouse.go
151
mouse.go
|
@ -1,151 +0,0 @@
|
|||
// Copyright 2014 The gocui Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/awesome-gocui/gocui"
|
||||
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// this function uses the mouse position to highlight & unhighlight things
|
||||
// this is run every time the user moves the mouse over the terminal window
|
||||
func mouseMove(g *gocui.Gui) {
|
||||
mx, my := g.MousePosition()
|
||||
for _, view := range g.Views() {
|
||||
view.Highlight = false
|
||||
}
|
||||
if v, err := g.ViewByPosition(mx, my); err == nil {
|
||||
v.Highlight = true
|
||||
}
|
||||
}
|
||||
|
||||
func msgDown(g *gocui.Gui, v *gocui.View) error {
|
||||
initialMouseX, initialMouseY = g.MousePosition()
|
||||
log.Log(NOW, "msgDown() X,Y", initialMouseX, initialMouseY)
|
||||
if vx, vy, _, _, err := g.ViewPosition("msg"); err == nil {
|
||||
xOffset = initialMouseX - vx
|
||||
yOffset = initialMouseY - vy
|
||||
msgMouseDown = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hideDDview() error {
|
||||
w, h := me.baseGui.MousePosition()
|
||||
log.Log(NOW, "hide dropdown menu() view msgMouseDown (w,h) =", w, h)
|
||||
if me.ddview == nil {
|
||||
return gocui.ErrUnknownView
|
||||
}
|
||||
if me.ddview.tk.v == nil {
|
||||
return gocui.ErrUnknownView
|
||||
}
|
||||
me.ddview.SetVisible(false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func showDDview() error {
|
||||
w, h := me.baseGui.MousePosition()
|
||||
log.Log(NOW, "show dropdown menu() view msgMouseDown (w,h) =", w, h)
|
||||
if me.ddview == nil {
|
||||
return gocui.ErrUnknownView
|
||||
}
|
||||
if me.ddview.tk.v == nil {
|
||||
return gocui.ErrUnknownView
|
||||
}
|
||||
me.ddview.SetVisible(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func mouseUp(g *gocui.Gui, v *gocui.View) error {
|
||||
w, h := g.MousePosition()
|
||||
log.Log(NOW, "mouseUp() view msgMouseDown (check here for dropdown menu click) (w,h) =", w, h)
|
||||
if me.ddClicked {
|
||||
me.ddClicked = false
|
||||
log.Log(NOW, "mouseUp() ddview is the thing that was clicked", w, h)
|
||||
log.Log(NOW, "mouseUp() find out what the string is here", w, h, me.ddview.tk.gocuiSize.h1)
|
||||
|
||||
var newZone string = ""
|
||||
if me.ddNode != nil {
|
||||
value := h - me.ddview.tk.gocuiSize.h0 - 1
|
||||
log.Log(NOW, "mouseUp() me.ddview.tk.gocuiSize.h1 =", me.ddview.tk.gocuiSize.h1)
|
||||
log.Log(NOW, "mouseUp() me.ddNode.vals =", me.ddNode.vals)
|
||||
valsLen := len(me.ddNode.vals)
|
||||
log.Log(NOW, "mouseUp() value =", value, "valsLen =", valsLen)
|
||||
log.Log(NOW, "mouseUp() me.ddNode.vals =", me.ddNode.vals)
|
||||
if (value >= 0) && (value < valsLen) {
|
||||
newZone = me.ddNode.vals[value]
|
||||
log.Log(NOW, "mouseUp() value =", value, "newZone =", newZone)
|
||||
}
|
||||
}
|
||||
hideDDview()
|
||||
if newZone != "" {
|
||||
if me.ddNode != nil {
|
||||
me.ddNode.SetText(newZone)
|
||||
me.ddNode.value = newZone
|
||||
me.ddNode.doUserEvent()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
/*
|
||||
// if there is a drop down view active, treat it like a dialog box and close it
|
||||
if (hideDDview() == nil) {
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
if msgMouseDown {
|
||||
msgMouseDown = false
|
||||
if movingMsg {
|
||||
movingMsg = false
|
||||
return nil
|
||||
} else {
|
||||
g.DeleteView("msg")
|
||||
}
|
||||
} else if globalMouseDown {
|
||||
globalMouseDown = false
|
||||
g.DeleteView("globalDown")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mouseDown(g *gocui.Gui, v *gocui.View) error {
|
||||
mx, my := g.MousePosition()
|
||||
if vx0, vy0, vx1, vy1, err := g.ViewPosition("msg"); err == nil {
|
||||
if mx >= vx0 && mx <= vx1 && my >= vy0 && my <= vy1 {
|
||||
return msgDown(g, v)
|
||||
}
|
||||
}
|
||||
globalMouseDown = true
|
||||
maxX, _ := g.Size()
|
||||
test := findUnderMouse()
|
||||
msg := fmt.Sprintf("Mouse really down at: %d,%d", mx, my) + "foobar"
|
||||
if test == me.ddview {
|
||||
if me.ddview.Visible() {
|
||||
log.Log(NOW, "hide DDview() Mouse really down at:", mx, my)
|
||||
hideDDview()
|
||||
} else {
|
||||
log.Log(NOW, "show DDview() Mouse really down at:", mx, my)
|
||||
showDDview()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
x := mx - len(msg)/2
|
||||
if x < 0 {
|
||||
x = 0
|
||||
} else if x+len(msg)+1 > maxX-1 {
|
||||
x = maxX - 1 - len(msg) - 1
|
||||
}
|
||||
log.Log(NOW, "mouseDown() about to write out message to 'globalDown' view. msg =", msg)
|
||||
if v, err := g.SetView("globalDown", x, my-1, x+len(msg)+1, my+1, 0); err != nil {
|
||||
if !errors.Is(err, gocui.ErrUnknownView) {
|
||||
return err
|
||||
}
|
||||
v.WriteString(msg)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func (tk *guiWidget) WidgetType() widget.WidgetType {
|
||||
if tk.node == nil {
|
||||
return widget.Label
|
||||
}
|
||||
return tk.node.WidgetType
|
||||
}
|
||||
|
||||
func (tk *guiWidget) WidgetId() int {
|
||||
return tk.node.WidgetId
|
||||
}
|
||||
|
||||
func (tk *guiWidget) GetLabel() string {
|
||||
return tk.node.GetLabel()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) IsEnabled() bool {
|
||||
return tk.node.IsEnabled()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) Checked() bool {
|
||||
return tk.node.State.Checked
|
||||
}
|
||||
|
||||
func (tk *guiWidget) Hidden() bool {
|
||||
if tk.node == nil {
|
||||
return false
|
||||
}
|
||||
if tk.parent == nil {
|
||||
return tk.node.Hidden()
|
||||
}
|
||||
if tk.parent.WidgetId() == 0 {
|
||||
return tk.node.Hidden()
|
||||
}
|
||||
if tk.parent.Hidden() {
|
||||
return true
|
||||
}
|
||||
return tk.node.Hidden()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) Direction() widget.Orientation {
|
||||
return tk.node.State.Direction
|
||||
}
|
||||
|
||||
func (tk *guiWidget) GridW() int {
|
||||
return tk.node.State.AtW
|
||||
}
|
||||
|
||||
func (tk *guiWidget) GridH() int {
|
||||
return tk.node.State.AtH
|
||||
}
|
||||
|
||||
func (tk *guiWidget) SetChecked(b bool) {
|
||||
tk.node.State.Checked = b
|
||||
}
|
340
place.go
340
place.go
|
@ -1,3 +1,6 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -7,119 +10,238 @@ import (
|
|||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func (n *node) placeBox(startW int, startH int) {
|
||||
if n.WidgetType != widget.Box {
|
||||
/*
|
||||
gocui defines the offset like this:
|
||||
|
||||
width -> increases to the right
|
||||
---------------------------------- hieght
|
||||
| H = 1 | increases
|
||||
| | |
|
||||
| W = 1 W = 18 | |
|
||||
| | v
|
||||
| H = 5 | downwards
|
||||
-------------------------------------
|
||||
*/
|
||||
|
||||
// moves the gocui view to the W and H offset on the screen
|
||||
func (tk *guiWidget) MoveToOffset(W, H int) {
|
||||
tk.gocuiSetWH(W, H)
|
||||
}
|
||||
|
||||
// returns where the corner of widget starts (upper left)
|
||||
func (tk *guiWidget) Position() (int, int) {
|
||||
return tk.gocuiSize.w0, tk.gocuiSize.h0
|
||||
}
|
||||
|
||||
func (w *guiWidget) placeBox(startW int, startH int) {
|
||||
if w.WidgetType() != widget.Box {
|
||||
return
|
||||
}
|
||||
n.showWidgetPlacement(true, "boxS()")
|
||||
|
||||
w.full.w0 = startW
|
||||
w.full.h0 = startH
|
||||
newW := startW
|
||||
newH := startH
|
||||
for _, child := range n.children {
|
||||
|
||||
for _, child := range w.children {
|
||||
sizeW, sizeH := child.Size()
|
||||
log.Log(INFO, "BOX START size(W,H) =", sizeW, sizeH, "new(W,H) =", newW, newH)
|
||||
child.placeWidgets(newW, newH)
|
||||
// n.showWidgetPlacement(logNow, "boxS()")
|
||||
newR := child.realGocuiSize()
|
||||
w := newR.w1 - newR.w0
|
||||
h := newR.h1 - newR.h0
|
||||
if n.direction == widget.Horizontal {
|
||||
log.Log(NOW, "BOX IS HORIZONTAL", n.progname, "newWH()", newW, newH, "child()", w, h, child.progname)
|
||||
// expand based on the child width
|
||||
newW += w
|
||||
} else {
|
||||
log.Log(NOW, "BOX IS VERTICAL ", n.progname, "newWH()", newW, newH, "child()", w, h, child.progname)
|
||||
// re-get the Size (they should not have changed, but maybe they can?)
|
||||
// TODO: figure this out or report that they did
|
||||
sizeW, sizeH = child.Size()
|
||||
if w.Direction() == widget.Vertical {
|
||||
log.Log(INFO, "BOX IS VERTICAL ", w.String(), "newWH()", newW, newH, "child()", sizeW, sizeH, child.String())
|
||||
// expand based on the child height
|
||||
newH += h
|
||||
newH += sizeH
|
||||
} else {
|
||||
log.Log(INFO, "BOX IS HORIZONTAL", w.String(), "newWH()", newW, newH, "child()", sizeW, sizeH, child.String())
|
||||
// expand based on the child width
|
||||
newW += sizeW
|
||||
}
|
||||
log.Log(INFO, "BOX END size(W,H) =", sizeW, sizeH, "new(W,H) =", newW, newH)
|
||||
}
|
||||
}
|
||||
|
||||
// just compute this every time?
|
||||
// newR := n.realGocuiSize()
|
||||
|
||||
n.showWidgetPlacement(true, "boxE()")
|
||||
func (tk *guiWidget) placeWidgets(startW int, startH int) (int, int) {
|
||||
if tk == nil {
|
||||
return 0, 0
|
||||
}
|
||||
if me.treeRoot == nil {
|
||||
return 0, 0
|
||||
}
|
||||
if tk.Hidden() {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func (n *node) placeWidgets(startW int, startH int) {
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
if me.rootNode == nil {
|
||||
return
|
||||
}
|
||||
tk.startW = startW
|
||||
tk.startH = startH
|
||||
|
||||
switch n.WidgetType {
|
||||
switch tk.WidgetType() {
|
||||
case widget.Window:
|
||||
for _, child := range n.children {
|
||||
child.placeWidgets(me.RawW, me.RawH)
|
||||
return
|
||||
tk.full.w0 = startW
|
||||
tk.full.h0 = startH
|
||||
startW += -2
|
||||
startH += 3
|
||||
for _, child := range tk.children {
|
||||
child.placeWidgets(startW, startH)
|
||||
sizeW, _ := child.Size()
|
||||
startW += sizeW + 4 // add the width to move the next widget over
|
||||
|
||||
}
|
||||
return startW, startH
|
||||
case widget.Tab:
|
||||
for _, child := range n.children {
|
||||
child.placeWidgets(me.RawW, me.RawH)
|
||||
return
|
||||
}
|
||||
case widget.Grid:
|
||||
n.placeGrid(startW, startH)
|
||||
// tk.dumpWidget(fmt.Sprintf("PlaceGridS(%d,%d)", startW, startH))
|
||||
// if you reset the values here, grid horizontal stacking doesn't work anymore
|
||||
// tk.widths = make(map[int]int) // how tall each row in the grid is
|
||||
// tk.heights = make(map[int]int) // how wide each column in the grid is
|
||||
newW, newH := tk.placeGrid(startW, startH)
|
||||
tk.full.w0 = newW
|
||||
tk.full.h0 = newH
|
||||
tk.full.w1 = newW
|
||||
tk.full.h1 = newH
|
||||
// tk.dumpWidget(fmt.Sprintf("PlaceGridE(%d,%d)", newW, newH))
|
||||
return newW, newH
|
||||
case widget.Box:
|
||||
n.placeBox(startW, startH)
|
||||
tk.placeBox(startW, startH)
|
||||
tk.full.w0 = startW
|
||||
tk.full.h0 = startH
|
||||
tk.full.w1 = startW
|
||||
tk.full.h1 = startH
|
||||
// tk.dumpWidget(fmt.Sprintf("PlaceBox(%d,%d)", startW, startH))
|
||||
return 0, 0
|
||||
case widget.Stdout:
|
||||
tk.setStdoutWH(startW, startH)
|
||||
return tk.gocuiSize.Width(), tk.gocuiSize.Height()
|
||||
case widget.Group:
|
||||
// move the group to the parent's next location
|
||||
n.gocuiSetWH(startW, startH)
|
||||
n.showWidgetPlacement(true, "group()")
|
||||
tk.gocuiSetWH(startW, startH)
|
||||
tk.full.w0 = startW
|
||||
tk.full.h0 = startH
|
||||
tk.full.w1 = startW
|
||||
tk.full.h1 = startH
|
||||
|
||||
newW := startW + me.GroupPadW
|
||||
newH := startH + 3 // normal hight of the group label
|
||||
newH := startH + 1 // normal hight of the group label
|
||||
var maxW int = 0
|
||||
// now move all the children aka: run place() on them
|
||||
for _, child := range n.children {
|
||||
child.placeWidgets(newW, newH)
|
||||
newR := child.realGocuiSize()
|
||||
for _, child := range tk.children {
|
||||
sizeW, sizeH := child.placeWidgets(newW, newH)
|
||||
// newR := child.realGocuiSize()
|
||||
// w := newR.w1 - newR.w0
|
||||
h := newR.h1 - newR.h0
|
||||
// h := newR.h1 - newR.h0
|
||||
|
||||
// increment straight down
|
||||
newH += h
|
||||
newH += sizeH + 1
|
||||
if sizeW > maxW {
|
||||
maxW = sizeW
|
||||
}
|
||||
log.Log(INFO, "REAL HEIGHT sizeW:", sizeW, "sizeH:", sizeH)
|
||||
}
|
||||
newH = newH - startH
|
||||
// tk.dumpWidget(fmt.Sprintf("PlaceGroup(%d,%d)", maxW, newH))
|
||||
return maxW, newH
|
||||
case widget.Button:
|
||||
if tk.isDense() && tk.isInGrid() {
|
||||
tk.frame = false
|
||||
// tk.color = nil
|
||||
// tk.defaultColor = nil
|
||||
/*
|
||||
if tk.IsEnabled() {
|
||||
tk.setColorButtonDense()
|
||||
} else {
|
||||
tk.setColorDisable()
|
||||
}
|
||||
*/
|
||||
// if tk.full.Height() > 0 {
|
||||
tk.full.h1 = tk.full.h0
|
||||
// }
|
||||
}
|
||||
tk.gocuiSetWH(startW, startH)
|
||||
return tk.gocuiSize.Width(), tk.gocuiSize.Height()
|
||||
default:
|
||||
n.gocuiSetWH(startW, startH)
|
||||
// n.moveTo(startW, startH)
|
||||
tk.gocuiSetWH(startW, startH)
|
||||
return tk.gocuiSize.Width(), tk.gocuiSize.Height()
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func (n *node) placeGrid(startW int, startH int) {
|
||||
w := n.tk
|
||||
n.showWidgetPlacement(true, "grid0:")
|
||||
if n.WidgetType != widget.Grid {
|
||||
return
|
||||
func (tk *guiWidget) isDense() bool {
|
||||
if tk.node.InTable() {
|
||||
return true
|
||||
}
|
||||
|
||||
return tk.isWindowDense()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) isWindowDense() bool {
|
||||
if tk.WidgetType() == widget.Window {
|
||||
return tk.window.dense
|
||||
}
|
||||
if tk.parent == nil {
|
||||
return true
|
||||
}
|
||||
return tk.parent.isWindowDense()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) isInGrid() bool {
|
||||
if tk.WidgetType() == widget.Grid {
|
||||
return true
|
||||
}
|
||||
if tk.parent == nil {
|
||||
return true
|
||||
}
|
||||
return tk.parent.isInGrid()
|
||||
}
|
||||
|
||||
func (w *guiWidget) placeGrid(startW int, startH int) (int, int) {
|
||||
// w.showWidgetPlacement("grid0:")
|
||||
if w.WidgetType() != widget.Grid {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
w.full.w0 = startW
|
||||
w.full.h0 = startH
|
||||
|
||||
// first compute the max sizes of the rows and columns
|
||||
for _, child := range n.children {
|
||||
newR := child.realGocuiSize()
|
||||
childW := newR.w1 - newR.w0
|
||||
childH := newR.h1 - newR.h0
|
||||
for _, child := range w.children {
|
||||
childW, childH := child.placeWidgets(child.startW, child.startH)
|
||||
|
||||
// set the child's realWidth, and grid offset
|
||||
if w.widths[child.AtW] < childW {
|
||||
w.widths[child.AtW] = childW
|
||||
if w.widths[child.GridW()] < childW {
|
||||
w.widths[child.GridW()] = childW
|
||||
}
|
||||
if w.heights[child.AtH] < childH {
|
||||
w.heights[child.AtH] = childH
|
||||
if w.heights[child.GridH()] < childH {
|
||||
w.heights[child.GridH()] = childH
|
||||
}
|
||||
if child.isDense() {
|
||||
if w.heights[child.GridH()] > 0 {
|
||||
w.heights[child.GridH()] = 1
|
||||
} else {
|
||||
w.heights[child.GridH()] = 0
|
||||
}
|
||||
// child.showWidgetPlacement(logInfo, "grid: ")
|
||||
log.Log(INFO, "placeGrid:", child.progname, "child()", childW, childH, "At()", child.AtW, child.AtH)
|
||||
}
|
||||
|
||||
// child.showWidgetPlacement("grid: ")
|
||||
log.Log(INFO, "placeGrid:", child.String(), "child()", childW, childH, "At()", child.GridW(), child.GridH())
|
||||
}
|
||||
|
||||
var maxW int = 0
|
||||
var maxH int = 0
|
||||
|
||||
// find the width and height offset of the grid for AtW,AtH
|
||||
for _, child := range n.children {
|
||||
child.showWidgetPlacement(true, "grid1:")
|
||||
for _, child := range w.children {
|
||||
// child.showWidgetPlacement("grid1:")
|
||||
|
||||
var totalW, totalH int
|
||||
for i, w := range w.widths {
|
||||
if i < child.AtW {
|
||||
if i < child.GridW() {
|
||||
totalW += w
|
||||
}
|
||||
}
|
||||
for i, h := range w.heights {
|
||||
if i < child.AtH {
|
||||
if i < child.GridH() {
|
||||
totalH += h
|
||||
}
|
||||
}
|
||||
|
@ -128,20 +250,32 @@ func (n *node) placeGrid(startW int, startH int) {
|
|||
newW := startW + totalW
|
||||
newH := startH + totalH
|
||||
|
||||
log.Log(INFO, "placeGrid:", child.progname, "new()", newW, newH, "At()", child.AtW, child.AtH)
|
||||
child.placeWidgets(newW, newH)
|
||||
child.showWidgetPlacement(true, "grid2:")
|
||||
if totalW > maxW {
|
||||
maxW = totalW
|
||||
}
|
||||
n.showWidgetPlacement(true, "grid3:")
|
||||
if totalH > maxH {
|
||||
maxH = totalH
|
||||
}
|
||||
|
||||
log.Log(INFO, "placeGrid:", child.String(), "new()", newW, newH, "At()", child.GridW(), child.GridH())
|
||||
child.placeWidgets(newW, newH)
|
||||
// child.showWidgetPlacement("grid2:")
|
||||
}
|
||||
// w.showWidgetPlacement("grid3:")
|
||||
w.full.w1 = startW + maxW
|
||||
w.full.h1 = startH + maxH
|
||||
return maxW, maxH
|
||||
}
|
||||
|
||||
// computes the real, actual size of all the gocli objects in a widget
|
||||
func (n *node) realGocuiSize() *rectType {
|
||||
var f func(n *node, r *rectType)
|
||||
func (w *guiWidget) realGocuiSize() *rectType {
|
||||
var f func(tk *guiWidget, r *rectType)
|
||||
newR := new(rectType)
|
||||
|
||||
outputW, outputH := w.Size()
|
||||
// initialize the values to opposite
|
||||
newR.w0 = 80
|
||||
newR.h0 = 24
|
||||
newR.w0 = outputW
|
||||
newR.h0 = outputH
|
||||
if me.baseGui != nil {
|
||||
maxW, maxH := me.baseGui.Size()
|
||||
newR.w0 = maxW
|
||||
|
@ -151,9 +285,9 @@ func (n *node) realGocuiSize() *rectType {
|
|||
newR.h1 = 0
|
||||
|
||||
// expand the rectangle to the biggest thing displayed
|
||||
f = func(n *node, r *rectType) {
|
||||
newR := n.tk.gocuiSize
|
||||
if !n.tk.isFake {
|
||||
f = func(tk *guiWidget, r *rectType) {
|
||||
newR := tk.gocuiSize
|
||||
if !tk.isFake {
|
||||
if r.w0 > newR.w0 {
|
||||
r.w0 = newR.w0
|
||||
}
|
||||
|
@ -167,18 +301,21 @@ func (n *node) realGocuiSize() *rectType {
|
|||
r.h1 = newR.h1
|
||||
}
|
||||
}
|
||||
for _, child := range n.children {
|
||||
for _, child := range tk.children {
|
||||
f(child, r)
|
||||
}
|
||||
}
|
||||
f(n, newR)
|
||||
f(w, newR)
|
||||
return newR
|
||||
}
|
||||
|
||||
func (n *node) textSize() (int, int) {
|
||||
/*
|
||||
func textSize(n *tree.Node) (int, int) {
|
||||
var tk *guiWidget
|
||||
tk = n.TK.(*guiWidget)
|
||||
var width, height int
|
||||
|
||||
for _, s := range strings.Split(widget.GetString(n.value), "\n") {
|
||||
for _, s := range strings.Split(widget.GetString(tk.value), "\n") {
|
||||
if width < len(s) {
|
||||
width = len(s)
|
||||
}
|
||||
|
@ -186,3 +323,46 @@ func (n *node) textSize() (int, int) {
|
|||
}
|
||||
return width, height
|
||||
}
|
||||
*/
|
||||
|
||||
func (tk *guiWidget) gocuiSetWH(sizeW, sizeH int) {
|
||||
w := len(tk.GetLabel())
|
||||
lines := strings.Split(tk.GetLabel(), "\n")
|
||||
h := len(lines)
|
||||
|
||||
if tk.Hidden() {
|
||||
tk.gocuiSize.w0 = 0
|
||||
tk.gocuiSize.h0 = 0
|
||||
tk.gocuiSize.w1 = 0
|
||||
tk.gocuiSize.h1 = 0
|
||||
return
|
||||
}
|
||||
|
||||
// tk := n.tk
|
||||
if tk.isFake {
|
||||
tk.gocuiSize.w0 = sizeW
|
||||
tk.gocuiSize.h0 = sizeH
|
||||
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW
|
||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH
|
||||
return
|
||||
}
|
||||
|
||||
if tk.frame {
|
||||
tk.gocuiSize.w0 = sizeW
|
||||
tk.gocuiSize.h0 = sizeH
|
||||
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW
|
||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH
|
||||
} else {
|
||||
tk.gocuiSize.w0 = sizeW - 1
|
||||
tk.gocuiSize.h0 = sizeH - 1
|
||||
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + 1
|
||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + 1
|
||||
}
|
||||
}
|
||||
|
||||
func (tk *guiWidget) setStdoutWH(sizeW, sizeH int) {
|
||||
tk.gocuiSize.w0 = sizeW
|
||||
tk.gocuiSize.h0 = sizeH
|
||||
tk.gocuiSize.w1 = tk.gocuiSize.w0 + me.stdout.w
|
||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + me.stdout.h
|
||||
}
|
||||
|
|
288
plugin.go
288
plugin.go
|
@ -1,117 +1,247 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
// if you include more than just this import
|
||||
// then your plugin might be doing something un-ideal (just a guess from 2023/02/27)
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/toolkits/tree"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func action(a *widget.Action) {
|
||||
log.Log(INFO, "action() START", a.WidgetId, a.ActionType, a.WidgetType, a.ProgName)
|
||||
n := me.rootNode.findWidgetId(a.WidgetId)
|
||||
var w *guiWidget
|
||||
if n != nil {
|
||||
w = n.tk
|
||||
func newAdd(n *tree.Node) {
|
||||
if n == nil {
|
||||
log.Warn("Tree Error: Add() sent n == nil")
|
||||
return
|
||||
}
|
||||
switch a.ActionType {
|
||||
case widget.Add:
|
||||
if w == nil {
|
||||
n := addNode(a)
|
||||
// w = n.tk
|
||||
n.addWidget()
|
||||
} else {
|
||||
if n.TK != nil {
|
||||
log.Log(INFO, "Tree Add() sent a widget we aleady seem to have")
|
||||
// this is done to protect the plugin being 'refreshed' with the
|
||||
// widget binary tree. TODO: find a way to keep them in sync
|
||||
log.Log(ERROR, "action() Add ignored for already defined widget",
|
||||
a.WidgetId, a.ActionType, a.WidgetType, a.ProgName)
|
||||
return
|
||||
}
|
||||
case widget.Show:
|
||||
if widget.GetBool(a.Value) {
|
||||
n.showView()
|
||||
n.TK = initWidget(n)
|
||||
if n.WidgetType == widget.Root {
|
||||
me.treeRoot = n
|
||||
}
|
||||
addWidget(n)
|
||||
/*
|
||||
TODO: removed while refactoring tree
|
||||
if w.enable {
|
||||
// don't change the color
|
||||
} else {
|
||||
n.hideWidgets()
|
||||
w = n.TK.(*guiWidget)
|
||||
}
|
||||
case widget.Set:
|
||||
if a.WidgetType == widget.Flag {
|
||||
log.Log(NOW, "TODO: set flag here", a.ActionType, a.WidgetType, a.ProgName)
|
||||
log.Log(NOW, "TODO: n.WidgetType =", n.WidgetType, "n.progname =", a.ProgName)
|
||||
} else {
|
||||
if a.Value == nil {
|
||||
log.Log(ERROR, "TODO: Set here. a == nil id =", a.WidgetId, "type =", a.WidgetType, "Name =", a.ProgName)
|
||||
log.Log(ERROR, "TODO: Set here. id =", a.WidgetId, "n.progname =", n.progname)
|
||||
} else {
|
||||
n.Set(a.Value)
|
||||
}
|
||||
}
|
||||
case widget.SetText:
|
||||
n.SetText(widget.GetString(a.Value))
|
||||
case widget.AddText:
|
||||
n.AddText(widget.GetString(a.Value))
|
||||
case widget.Move:
|
||||
log.Log(NOW, "attempt to move() =", a.ActionType, a.WidgetType, a.ProgName)
|
||||
case widget.ToolkitClose:
|
||||
log.Log(NOW, "attempting to close the plugin and release stdout and stderr")
|
||||
standardExit()
|
||||
case widget.Enable:
|
||||
if n.Visible() {
|
||||
// widget was already shown
|
||||
} else {
|
||||
log.Log(INFO, "Setting Visable to true", a.ProgName)
|
||||
n.SetVisible(true)
|
||||
}
|
||||
case widget.Disable:
|
||||
if n.Visible() {
|
||||
log.Log(INFO, "Setting Visable to false", a.ProgName)
|
||||
n.SetVisible(false)
|
||||
} else {
|
||||
// widget was already hidden
|
||||
}
|
||||
default:
|
||||
log.Log(ERROR, "action() ActionType =", a.ActionType, "WidgetType =", a.WidgetType, "Name =", a.ProgName)
|
||||
}
|
||||
log.Log(INFO, "action() END")
|
||||
*/
|
||||
w := n.TK.(*guiWidget)
|
||||
w.Show()
|
||||
me.refresh = true // testing code to see if refresh can work
|
||||
}
|
||||
|
||||
func (n *node) AddText(text string) {
|
||||
// for gocui as a GUI plugin, SetTitle & SetLabel are identical to SetText
|
||||
func setTitle(n *tree.Node, s string) {
|
||||
setText(n, s)
|
||||
me.refresh = true // testing code to see if refresh can work
|
||||
}
|
||||
|
||||
func setLabel(n *tree.Node, s string) {
|
||||
setText(n, s)
|
||||
me.refresh = true // testing code to see if refresh can work
|
||||
}
|
||||
|
||||
// setText() and addText() are simple. They take the event sent
|
||||
// to the GO plugin from the application and lookup the plugin structure
|
||||
// then pass that event to gocui. This is the transfer point
|
||||
func setText(n *tree.Node, s string) {
|
||||
if n == nil {
|
||||
log.Warn("Tree Error: Add() sent n == nil")
|
||||
return
|
||||
}
|
||||
if n.TK == nil {
|
||||
log.Warn("Tree sent an action on a widget we didn't seem to have.")
|
||||
return
|
||||
}
|
||||
w := n.TK.(*guiWidget)
|
||||
w.SetText(s)
|
||||
me.refresh = true // testing code to see if refresh can work
|
||||
}
|
||||
|
||||
func addText(n *tree.Node, s string) {
|
||||
if n == nil {
|
||||
log.Warn("Tree Error: Add() sent n == nil")
|
||||
return
|
||||
}
|
||||
if n.TK == nil {
|
||||
log.Warn("Tree sent an action on a widget we didn't seem to have.")
|
||||
return
|
||||
}
|
||||
w := n.TK.(*guiWidget)
|
||||
w.AddText(s)
|
||||
me.refresh = true // testing code to see if refresh can work
|
||||
}
|
||||
|
||||
func (w *guiWidget) deleteGocuiViews() {
|
||||
if w.v == nil {
|
||||
// no gocui view to delete for this widget
|
||||
} else {
|
||||
me.baseGui.DeleteView(w.cuiName)
|
||||
w.v = nil
|
||||
}
|
||||
for _, child := range w.children {
|
||||
child.deleteGocuiViews()
|
||||
}
|
||||
}
|
||||
|
||||
func (w *guiWidget) deleteNode() {
|
||||
p := w.parent
|
||||
for i, child := range p.children {
|
||||
log.Log(NOW, "parent has child:", i, child.cuiName, child.String())
|
||||
if w == child {
|
||||
log.Log(NOW, "Found child ==", i, child.cuiName, child.String())
|
||||
log.Log(NOW, "Found n ==", i, w.cuiName, w.String())
|
||||
p.children = append(p.children[:i], p.children[i+1:]...)
|
||||
}
|
||||
}
|
||||
for i, child := range p.children {
|
||||
log.Log(NOW, "parent now has child:", i, child.cuiName, child.String())
|
||||
}
|
||||
w.deleteGocuiViews()
|
||||
}
|
||||
|
||||
func (w *guiWidget) AddText(text string) {
|
||||
if w == nil {
|
||||
log.Log(NOW, "widget is nil")
|
||||
return
|
||||
}
|
||||
n.vals = append(n.vals, text)
|
||||
for i, s := range n.vals {
|
||||
log.Log(NOW, "AddText()", n.progname, i, s)
|
||||
w.vals = append(w.vals, text)
|
||||
for i, s := range w.vals {
|
||||
log.Log(INFO, "AddText()", w.String(), i, s)
|
||||
}
|
||||
n.SetText(text)
|
||||
w.SetText(text)
|
||||
}
|
||||
|
||||
func (n *node) SetText(text string) {
|
||||
func (tk *guiWidget) SetText(text string) {
|
||||
var changed bool = false
|
||||
if n == nil {
|
||||
if tk == nil {
|
||||
log.Log(NOW, "widget is nil")
|
||||
return
|
||||
}
|
||||
if widget.GetString(n.value) != text {
|
||||
n.value = text
|
||||
if tk.labelN != text {
|
||||
tk.labelN = text
|
||||
changed = true
|
||||
}
|
||||
tk.node.State.Label = text
|
||||
if !changed {
|
||||
return
|
||||
}
|
||||
|
||||
if n.Visible() {
|
||||
n.textResize()
|
||||
n.deleteView()
|
||||
n.showView()
|
||||
if tk.Visible() {
|
||||
tk.textResize()
|
||||
tk.Hide()
|
||||
tk.Show()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) Set(val any) {
|
||||
// w := n.tk
|
||||
log.Log(INFO, "Set() value =", val)
|
||||
func (tk *guiWidget) GetText() string {
|
||||
if tk == nil {
|
||||
log.Log(NOW, "widget is nil")
|
||||
return ""
|
||||
}
|
||||
// deprecate this
|
||||
if tk.labelN != "" {
|
||||
return tk.labelN
|
||||
}
|
||||
if tk.node == nil {
|
||||
// return gocui.view name?
|
||||
return tk.cuiName
|
||||
}
|
||||
if tk.GetLabel() != "" {
|
||||
return tk.GetLabel()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
n.value = val
|
||||
if n.WidgetType != widget.Checkbox {
|
||||
n.setCheckbox(val)
|
||||
// hack. use "textbox widget" to "disable" user events
|
||||
func hideDisable() {
|
||||
if me.textbox.tk == nil {
|
||||
initTextbox()
|
||||
}
|
||||
|
||||
me.textbox.tk.Hide()
|
||||
me.textbox.tk.enable = false
|
||||
me.textbox.tk.node.State.Enable = false
|
||||
me.textbox.active = false
|
||||
me.baseGui.SetCurrentView("help")
|
||||
// me.baseGui.DeleteView(me.textbox.tk.cuiName)
|
||||
// me.baseGui.DeleteView(me.textbox.tk.v.Name())
|
||||
}
|
||||
|
||||
// hack. use "textbox widget" to "disable" user events
|
||||
func showDisable() {
|
||||
if me.textbox.tk == nil {
|
||||
initTextbox()
|
||||
me.textbox.tk.prepTextbox()
|
||||
}
|
||||
r := new(rectType)
|
||||
r.w0 = 2
|
||||
r.h0 = 1
|
||||
r.w1 = r.w0 + 24
|
||||
r.h1 = r.h0 + 2
|
||||
me.textbox.tk.forceSizes(r)
|
||||
me.textbox.tk.Show() // actually makes the gocui view. TODO: redo this
|
||||
// log.Info("textbox should be shown")
|
||||
// showTextbox("Running...")
|
||||
// me.textbox.tk.dumpWidget("shown?")
|
||||
|
||||
me.textbox.tk.setColorModal()
|
||||
me.textbox.tk.v.Clear()
|
||||
me.textbox.tk.v.WriteString("Running...")
|
||||
|
||||
me.textbox.tk.v.Editable = true
|
||||
me.textbox.tk.v.Wrap = true
|
||||
|
||||
me.textbox.tk.SetViewRect(r)
|
||||
me.baseGui.SetCurrentView(me.textbox.tk.v.Name())
|
||||
|
||||
// bind the enter key to a function so we can close the textbox
|
||||
me.baseGui.SetKeybinding(me.textbox.tk.v.Name(), gocui.KeyEnter, gocui.ModNone, theCloseTheTextbox)
|
||||
|
||||
me.textbox.active = true
|
||||
}
|
||||
|
||||
func (tk *guiWidget) Disable() {
|
||||
if tk == nil {
|
||||
log.Log(NOW, "widget is nil")
|
||||
return
|
||||
}
|
||||
|
||||
switch tk.WidgetType() {
|
||||
case widget.Box:
|
||||
showDisable()
|
||||
return
|
||||
case widget.Button:
|
||||
tk.setColorDisable()
|
||||
return
|
||||
default:
|
||||
tk.dumpWidget("fixme: disable")
|
||||
}
|
||||
}
|
||||
|
||||
func (tk *guiWidget) Enable() {
|
||||
if tk == nil {
|
||||
log.Log(NOW, "widget is nil")
|
||||
return
|
||||
}
|
||||
|
||||
switch tk.WidgetType() {
|
||||
case widget.Box:
|
||||
hideDisable()
|
||||
return
|
||||
case widget.Button:
|
||||
tk.restoreEnableColor()
|
||||
return
|
||||
default:
|
||||
tk.dumpWidget("fixme: enable")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
var outputW int = 180
|
||||
var outputH int = 24
|
||||
|
||||
func moveMsg(g *gocui.Gui) {
|
||||
mx, my := g.MousePosition()
|
||||
if !movingMsg && (mx != initialMouseX || my != initialMouseY) {
|
||||
movingMsg = true
|
||||
}
|
||||
g.SetView("msg", mx-xOffset, my-yOffset, mx-xOffset+outputW, my-yOffset+outputH+me.FramePadH, 0)
|
||||
g.SetViewOnBottom("msg")
|
||||
}
|
||||
|
||||
func showMsg(g *gocui.Gui, v *gocui.View) error {
|
||||
var l string
|
||||
var err error
|
||||
|
||||
log.Log(NOW, "showMsg() v.name =", v.Name())
|
||||
if _, err := g.SetCurrentView(v.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, cy := v.Cursor()
|
||||
if l, err = v.Line(cy); err != nil {
|
||||
l = ""
|
||||
}
|
||||
|
||||
makeOutputWidget(g, l)
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View {
|
||||
maxX, maxY := g.Size()
|
||||
|
||||
if me.rootNode == nil {
|
||||
// keep skipping this until the binary tree is initialized
|
||||
return nil
|
||||
}
|
||||
|
||||
if me.logStdout == nil {
|
||||
a := new(widget.Action)
|
||||
a.ProgName = "stdout"
|
||||
a.WidgetType = widget.Stdout
|
||||
a.WidgetId = -3
|
||||
a.ParentId = 0
|
||||
n := addNode(a)
|
||||
me.logStdout = n
|
||||
me.logStdout.tk.gocuiSize.w0 = maxX - 32
|
||||
me.logStdout.tk.gocuiSize.h0 = maxY / 2
|
||||
me.logStdout.tk.gocuiSize.w1 = me.logStdout.tk.gocuiSize.w0 + outputW
|
||||
me.logStdout.tk.gocuiSize.h1 = me.logStdout.tk.gocuiSize.h0 + outputH
|
||||
}
|
||||
v, err := g.View("msg")
|
||||
if v == nil {
|
||||
log.Log(NOW, "makeoutputwindow() this is supposed to happen. v == nil", err)
|
||||
} else {
|
||||
log.Log(NOW, "makeoutputwindow() msg != nil. WTF now? err =", err)
|
||||
}
|
||||
|
||||
// help, err := g.SetView("help", maxX-32, 0, maxX-1, 13, 0)
|
||||
// v, err = g.SetView("msg", 3, 3, 30, 30, 0)
|
||||
|
||||
v, err = g.SetView("msg", maxX-32, maxY/2, maxX/2+outputW, maxY/2+outputH, 0)
|
||||
if errors.Is(err, gocui.ErrUnknownView) {
|
||||
log.Log(NOW, "makeoutputwindow() this is supposed to happen?", err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Log(NOW, "makeoutputwindow() create output window failed", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if v == nil {
|
||||
log.Log(NOW, "makeoutputwindow() msg == nil. WTF now? err =", err)
|
||||
return nil
|
||||
} else {
|
||||
me.logStdout.tk.v = v
|
||||
}
|
||||
|
||||
v.Clear()
|
||||
v.SelBgColor = gocui.ColorCyan
|
||||
v.SelFgColor = gocui.ColorBlack
|
||||
fmt.Fprintln(v, "figure out how to capture STDOUT to here\n"+stringFromMouseClick)
|
||||
g.SetViewOnBottom("msg")
|
||||
// g.SetViewOnBottom(v.Name())
|
||||
return v
|
||||
}
|
|
@ -0,0 +1,408 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func (tk *guiWidget) Size() (int, int) {
|
||||
if tk == nil {
|
||||
return 0, 0
|
||||
}
|
||||
if me.treeRoot == nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// don't count hidden widgets in size calculations
|
||||
if tk.Hidden() {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
switch tk.WidgetType() {
|
||||
case widget.Window:
|
||||
var maxH int = 0
|
||||
var maxW int = 0
|
||||
for _, child := range tk.children {
|
||||
if tk.Hidden() {
|
||||
continue
|
||||
}
|
||||
sizeW, sizeH := child.Size()
|
||||
maxW += sizeW
|
||||
if sizeH > maxH {
|
||||
maxH = sizeH
|
||||
}
|
||||
|
||||
}
|
||||
return maxW, maxH
|
||||
case widget.Grid:
|
||||
return tk.sizeGrid()
|
||||
case widget.Box:
|
||||
return tk.sizeBox()
|
||||
case widget.Group:
|
||||
// move the group to the parent's next location
|
||||
maxW := tk.gocuiSize.Width()
|
||||
maxH := tk.gocuiSize.Height()
|
||||
|
||||
for _, child := range tk.children {
|
||||
if tk.Hidden() {
|
||||
continue
|
||||
}
|
||||
sizeW, sizeH := child.Size()
|
||||
|
||||
// increment straight down
|
||||
maxH += sizeH
|
||||
if sizeW > maxW {
|
||||
maxW = sizeW
|
||||
}
|
||||
}
|
||||
return maxW + me.GroupPadW + 3, maxH
|
||||
case widget.Label:
|
||||
return len(tk.String()) + 2, 1
|
||||
case widget.Textbox:
|
||||
return len(tk.String()) + 10, 3 // TODO: compute this based on 'window dense'
|
||||
case widget.Checkbox:
|
||||
return len(tk.String()) + 2, 3 // TODO: compute this based on 'window dense'
|
||||
case widget.Button:
|
||||
if tk.isDense() {
|
||||
return len(tk.String()) + 2, 0
|
||||
}
|
||||
return len(tk.String()) + 2, 3 // TODO: compute this based on 'window dense'
|
||||
}
|
||||
if tk.isFake {
|
||||
return 0, 0
|
||||
}
|
||||
return len(tk.String()), 3
|
||||
}
|
||||
|
||||
func (w *guiWidget) sizeGrid() (int, int) {
|
||||
if w.Hidden() {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// first compute the max sizes of the rows and columns
|
||||
for _, child := range w.children {
|
||||
if w.Hidden() {
|
||||
continue
|
||||
}
|
||||
sizeW, sizeH := child.Size()
|
||||
|
||||
// set the child's realWidth, and grid offset
|
||||
if w.widths[child.GridW()] < sizeW {
|
||||
w.widths[child.GridW()] = sizeW
|
||||
}
|
||||
if w.heights[child.GridH()] < sizeH {
|
||||
w.heights[child.GridH()] = sizeH
|
||||
}
|
||||
}
|
||||
|
||||
// find the width and height offset of the grid for AtW,AtH
|
||||
var totalW int = 0
|
||||
var totalH int = 0
|
||||
for _, width := range w.widths {
|
||||
totalW += width
|
||||
}
|
||||
for _, h := range w.heights {
|
||||
totalH += h
|
||||
}
|
||||
return totalW + me.GridPadW, totalH
|
||||
}
|
||||
|
||||
func (w *guiWidget) sizeBox() (int, int) {
|
||||
if w.WidgetType() != widget.Box {
|
||||
return 0, 0
|
||||
}
|
||||
if w.Hidden() {
|
||||
return 0, 0
|
||||
}
|
||||
var maxW int = 0
|
||||
var maxH int = 0
|
||||
|
||||
for _, child := range w.children {
|
||||
if w.Hidden() {
|
||||
continue
|
||||
}
|
||||
sizeW, sizeH := child.Size()
|
||||
if child.Direction() == widget.Vertical {
|
||||
maxW += sizeW
|
||||
if sizeH > maxH {
|
||||
maxH = sizeH
|
||||
}
|
||||
} else {
|
||||
maxH += sizeH
|
||||
if sizeW > maxW {
|
||||
maxW = sizeW
|
||||
}
|
||||
}
|
||||
}
|
||||
return maxW + me.BoxPadW, maxH
|
||||
}
|
||||
|
||||
/*
|
||||
var wtf bool
|
||||
|
||||
func (tk *guiWidget) verifyRect() bool {
|
||||
if !tk.Visible() {
|
||||
// log.Info("verifyRect() tk is not visible", tk.cuiName)
|
||||
return false
|
||||
}
|
||||
vw0, vh0, vw1, vh1, err := me.baseGui.ViewPosition(tk.cuiName)
|
||||
if err != nil {
|
||||
// log.Printf("verifyRect() gocui err=%v cuiName=%s v.Name=%s", err, tk.cuiName, tk.v.Name())
|
||||
vw0, vh0, vw1, vh1, err = me.baseGui.ViewPosition(tk.v.Name())
|
||||
if err != nil {
|
||||
log.Printf("verifyRect() ACTUAL FAIL gocui err=%v cuiName=%s v.Name=%s", err, tk.cuiName, tk.v.Name())
|
||||
return false
|
||||
}
|
||||
// return false
|
||||
}
|
||||
var ok bool = true
|
||||
if vw0 != tk.full.w0 {
|
||||
// log.Info("verifyRect() FIXING w0", tk.cuiName, vw0, vw1, vh0, vh1, tk.gocuiSize.w0, "w0 =", vw0)
|
||||
tk.full.w0 = vw0
|
||||
ok = false
|
||||
}
|
||||
if vw1 != tk.full.w1 {
|
||||
// log.Info("verifyRect() FIXING w1", tk.cuiName, vw0, vw1, vh0, vh1, tk.gocuiSize.w1, "w1 =", vw1)
|
||||
tk.full.w1 = vw1
|
||||
ok = false
|
||||
}
|
||||
if vh0 != tk.full.h0 {
|
||||
// log.Info("verifyRect() FIXING h0", tk.cuiName, vw0, vw1, vh0, vh1, tk.gocuiSize.h0)
|
||||
tk.full.h0 = vh0
|
||||
ok = false
|
||||
}
|
||||
if vh1 != tk.full.h1 {
|
||||
// log.Info("verifyRect() FIXING h1", tk.cuiName, vw0, vw1, vh0, vh1, tk.gocuiSize.h1)
|
||||
tk.full.h1 = vh1
|
||||
ok = false
|
||||
}
|
||||
if !ok {
|
||||
// log.Info("verifyRect() NEED TO FIX RECT HERE", tk.cuiName)
|
||||
// tk.dumpWidget("verifyRect() FIXME")
|
||||
}
|
||||
// log.Printf("verifyRect() OK cuiName=%s v.Name=%s", tk.cuiName, tk.v.Name())
|
||||
return true
|
||||
}
|
||||
*/
|
||||
|
||||
func (tk *guiWidget) setFullSize() bool {
|
||||
r := tk.getFullSize()
|
||||
|
||||
if tk.Hidden() {
|
||||
p := tk.parent
|
||||
if p != nil {
|
||||
// tk.full.w0 = p.full.w0
|
||||
// tk.full.w1 = p.full.w1
|
||||
// tk.full.h0 = p.full.h0
|
||||
// tk.full.h1 = p.full.h1
|
||||
tk.full.w0 = 0
|
||||
tk.full.w1 = 0
|
||||
tk.full.h0 = 0
|
||||
tk.full.h1 = 0
|
||||
} else {
|
||||
tk.full.w0 = 0
|
||||
tk.full.w1 = 0
|
||||
tk.full.h0 = 0
|
||||
tk.full.h1 = 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var changed bool
|
||||
if tk.full.w0 != r.w0 {
|
||||
tk.full.w0 = r.w0
|
||||
changed = true
|
||||
}
|
||||
// widget might be forced to a certain location
|
||||
if tk.full.w0 < tk.force.w0 {
|
||||
tk.gocuiSize.w0 = tk.force.w0
|
||||
tk.full.w0 = tk.force.w0
|
||||
changed = false
|
||||
}
|
||||
if tk.full.w1 != r.w1 {
|
||||
tk.full.w1 = r.w1
|
||||
changed = true
|
||||
}
|
||||
if tk.full.h0 != r.h0 {
|
||||
tk.full.h0 = r.h0
|
||||
changed = true
|
||||
}
|
||||
// widget might be forced to a certain location
|
||||
if tk.full.h0 < tk.force.h0 {
|
||||
tk.gocuiSize.h0 = tk.force.h0
|
||||
tk.full.h0 = tk.force.h0
|
||||
changed = false
|
||||
}
|
||||
if tk.full.h1 != r.h1 {
|
||||
tk.full.h1 = r.h1
|
||||
changed = true
|
||||
}
|
||||
if tk.WidgetType() == widget.Button {
|
||||
tk.full.h1 = tk.full.h0 + 1
|
||||
}
|
||||
if tk.isDense() && tk.isInGrid() {
|
||||
tk.full.h1 = tk.full.h0
|
||||
}
|
||||
if changed {
|
||||
tk.dumpWidget(fmt.Sprintf("setFullSize(changed)"))
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
func (tk *guiWidget) gridFullSize() rectType {
|
||||
var r rectType
|
||||
var first bool = true
|
||||
for _, child := range tk.children {
|
||||
cr := child.getFullSize()
|
||||
if cr.Width() == 0 && cr.Height() == 0 {
|
||||
// ignore widgets of zero size?
|
||||
continue
|
||||
}
|
||||
if first {
|
||||
// use the lowest width and hight from children widgets
|
||||
r.w0 = cr.w0
|
||||
r.h0 = cr.h0
|
||||
r.w1 = cr.w1
|
||||
r.h1 = cr.h1
|
||||
first = false
|
||||
// child.dumpWidget(fmt.Sprintf("grid(f)"))
|
||||
continue
|
||||
}
|
||||
// child.dumpWidget(fmt.Sprintf("grid()"))
|
||||
// use the lowest width and hight from children widgets
|
||||
if r.w0 > cr.w0 {
|
||||
r.w0 = cr.w0
|
||||
}
|
||||
if r.h0 > cr.h0 {
|
||||
r.h0 = cr.h0
|
||||
}
|
||||
// use the highest width and hight from children widgets
|
||||
if r.w1 < cr.w1 {
|
||||
r.w1 = cr.w1
|
||||
}
|
||||
if r.h1 < cr.h1 {
|
||||
r.h1 = cr.h1
|
||||
}
|
||||
}
|
||||
tk.full.w0 = r.w0
|
||||
tk.full.w1 = r.w1
|
||||
tk.full.h0 = r.h0
|
||||
tk.full.h1 = r.h1
|
||||
return r
|
||||
}
|
||||
|
||||
func (tk *guiWidget) buttonFullSize() rectType {
|
||||
var r rectType
|
||||
r.w0 = tk.gocuiSize.w0
|
||||
r.w1 = tk.gocuiSize.w1
|
||||
r.h0 = tk.gocuiSize.h0
|
||||
r.h1 = tk.gocuiSize.h1
|
||||
|
||||
// try setting the full values here ? is this right?
|
||||
tk.full.w0 = r.w0
|
||||
tk.full.w1 = r.w1
|
||||
tk.full.h0 = r.h0
|
||||
tk.full.h1 = r.h1
|
||||
|
||||
// total hack. fix this somewhere eventually correctly
|
||||
if tk.isDense() { // total hack. fix this somewhere eventually correctly
|
||||
tk.full.h0 += 1 // total hack. fix this somewhere eventually correctly
|
||||
tk.full.h1 = tk.full.h0 // total hack. fix this somewhere eventually correctly
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// this checks a widget to see if it is under (W,H), then checks the widget's children
|
||||
// anything that matches is passed back as an array of widgets
|
||||
func (tk *guiWidget) getFullSize() rectType {
|
||||
var r rectType
|
||||
|
||||
if tk.Hidden() {
|
||||
/*
|
||||
p := tk.parent
|
||||
if p != nil {
|
||||
return p.full
|
||||
}
|
||||
*/
|
||||
var r rectType
|
||||
r.w0 = 0
|
||||
r.w1 = 0
|
||||
r.h0 = 0
|
||||
r.h1 = 0
|
||||
return r
|
||||
}
|
||||
|
||||
if tk.WidgetType() == widget.Grid {
|
||||
return tk.gridFullSize()
|
||||
}
|
||||
|
||||
// these are 'simple' widgets
|
||||
// the full size is exactly what gocui uses
|
||||
switch tk.WidgetType() {
|
||||
case widget.Label:
|
||||
r := tk.buttonFullSize()
|
||||
r.w1 += 5
|
||||
return r
|
||||
case widget.Button:
|
||||
r := tk.buttonFullSize()
|
||||
r.w1 += 5
|
||||
return r
|
||||
case widget.Checkbox:
|
||||
return tk.buttonFullSize()
|
||||
case widget.Dropdown:
|
||||
r := tk.buttonFullSize()
|
||||
r.w1 += 7 // TODO: fix this to be real
|
||||
return r
|
||||
default:
|
||||
}
|
||||
|
||||
if tk.v == nil {
|
||||
r.w0 = tk.full.w0
|
||||
r.w1 = tk.full.w1
|
||||
r.h0 = tk.full.h0
|
||||
r.h1 = tk.full.h1
|
||||
} else {
|
||||
r.w0 = tk.gocuiSize.w0
|
||||
r.w1 = tk.gocuiSize.w1
|
||||
r.h0 = tk.gocuiSize.h0
|
||||
r.h1 = tk.gocuiSize.h1
|
||||
}
|
||||
|
||||
// search through the children widgets in the binary tree
|
||||
for _, child := range tk.children {
|
||||
cr := child.getFullSize()
|
||||
/* this didn't make things work either
|
||||
if child.v == nil {
|
||||
continue
|
||||
}
|
||||
*/
|
||||
// use the lowest width and hight from children widgets
|
||||
if r.w0 > cr.w0 {
|
||||
r.w0 = cr.w0
|
||||
}
|
||||
if r.h0 > cr.h0 {
|
||||
r.h0 = cr.h0
|
||||
}
|
||||
// use the highest width and hight from children widgets
|
||||
if r.w1 < cr.w1 {
|
||||
r.w1 = cr.w1
|
||||
}
|
||||
if r.h1 < cr.h1 {
|
||||
r.h1 = cr.h1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// try setting the full values here ? is this right?
|
||||
tk.full.w0 = r.w0
|
||||
tk.full.w1 = r.w1
|
||||
tk.full.h0 = r.h0
|
||||
tk.full.h1 = r.h1
|
||||
|
||||
return r
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func createStdout(g *gocui.Gui) bool {
|
||||
if me.stdout.tk == nil {
|
||||
makeOutputWidget(g, "this is a create before a mouse click")
|
||||
// me.logStdout.v.Write([]byte(msg))
|
||||
|
||||
// this will show very early debugging output
|
||||
// keep this code commented out but do not remove it. when it doubt, this will be the Light of Elendil
|
||||
// NEVER REMOVE THIS CODE
|
||||
|
||||
msg := fmt.Sprintf("test out gocuiEvent() %d\n", me.ecount)
|
||||
me.stdout.tk.Write([]byte(msg))
|
||||
|
||||
log.Log(NOW, "logStdout test out")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func coreStdout() {
|
||||
if me.stdout.tk != nil {
|
||||
return
|
||||
}
|
||||
a := new(widget.Action)
|
||||
a.ProgName = "2stdout2"
|
||||
a.WidgetType = widget.Stdout
|
||||
a.WidgetId = me.stdout.wId
|
||||
a.ParentId = 0
|
||||
// n := addNode(a)
|
||||
n := me.myTree.AddNode(a)
|
||||
me.stdout.tk = initWidget(n)
|
||||
|
||||
tk := me.stdout.tk
|
||||
tk.cuiName = "msg"
|
||||
tk.gocuiSize.w0 = me.stdout.lastW
|
||||
tk.gocuiSize.h0 = me.stdout.lastH
|
||||
tk.gocuiSize.w1 = tk.gocuiSize.w0 + me.stdout.w
|
||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + me.stdout.h
|
||||
}
|
||||
|
||||
func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View {
|
||||
if me.treeRoot == nil {
|
||||
// keep skipping this until the binary tree is initialized
|
||||
return nil
|
||||
}
|
||||
|
||||
coreStdout()
|
||||
|
||||
if me.stdout.tk == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
me.stdout.tk.cuiName = "msg"
|
||||
me.stdout.tk.SetView()
|
||||
|
||||
v, err := g.View("msg")
|
||||
if v == nil {
|
||||
// log.Log(NOW, "makeoutputwindow() this is supposed to happen. v == nil", err)
|
||||
} else {
|
||||
log.Log(NOW, "makeoutputwindow() msg != nil. WTF now? err =", err)
|
||||
return v
|
||||
}
|
||||
|
||||
v = me.stdout.tk.v
|
||||
|
||||
v.Clear()
|
||||
v.SelBgColor = gocui.ColorCyan
|
||||
v.SelFgColor = gocui.ColorBlack
|
||||
fmt.Fprintln(v, "figure out how to capture STDOUT to here\n"+stringFromMouseClick)
|
||||
// g.SetViewOnBottom("msg")
|
||||
// setBottomBG()
|
||||
|
||||
me.stdout.tk.DrawAt(me.stdout.lastW, me.stdout.lastH)
|
||||
relocateStdoutOffscreen()
|
||||
return v
|
||||
}
|
||||
|
||||
func relocateStdoutOffscreen() {
|
||||
if me.stdout.tk == nil {
|
||||
return
|
||||
}
|
||||
if !me.stdout.disable {
|
||||
log.Info("Using gocui STDOUT")
|
||||
log.CaptureMode(me.stdout.tk)
|
||||
}
|
||||
newW := 10
|
||||
newH := 0 - me.stdout.h - 4
|
||||
me.stdout.tk.relocateStdout(newW, newH)
|
||||
}
|
||||
|
||||
func (tk *guiWidget) relocateStdout(w int, h int) {
|
||||
if me.stdout.w < 8 {
|
||||
me.stdout.w = 8
|
||||
}
|
||||
|
||||
if me.stdout.h < 4 {
|
||||
me.stdout.h = 4
|
||||
}
|
||||
|
||||
if w+me.stdout.w < 2 {
|
||||
w = 2
|
||||
}
|
||||
|
||||
if h+me.stdout.h < 2 {
|
||||
h = 2
|
||||
}
|
||||
|
||||
w0 := w
|
||||
h0 := h
|
||||
w1 := w + me.stdout.w
|
||||
h1 := h + me.stdout.h
|
||||
|
||||
tk.gocuiSize.w0 = w0
|
||||
tk.gocuiSize.w1 = w1
|
||||
tk.gocuiSize.h0 = h0
|
||||
tk.gocuiSize.h1 = h1
|
||||
|
||||
tk.full.w0 = w0
|
||||
tk.full.w1 = w1
|
||||
tk.full.h0 = h0
|
||||
tk.full.h1 = h1
|
||||
|
||||
tk.SetView()
|
||||
}
|
||||
|
||||
// from the gocui devs:
|
||||
// Write appends a byte slice into the view's internal buffer. Because
|
||||
// View implements the io.Writer interface, it can be passed as parameter
|
||||
// of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must
|
||||
// be called to clear the view's buffer.
|
||||
|
||||
func (w stdout) Height() int {
|
||||
if w.tk == nil {
|
||||
return 40
|
||||
}
|
||||
return w.tk.gocuiSize.Height() - 2
|
||||
}
|
||||
|
||||
func (w stdout) Write(p []byte) (n int, err error) {
|
||||
me.writeMutex.Lock()
|
||||
defer me.writeMutex.Unlock()
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(string(p)), "\n")
|
||||
me.stdout.outputS = append(me.stdout.outputS, lines...)
|
||||
if me.outf != nil {
|
||||
fmt.Fprint(me.outf, string(p))
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (w *guiWidget) Write(p []byte) (n int, err error) {
|
||||
lines := strings.Split(strings.TrimSpace(string(p)), "\n")
|
||||
if me.outf != nil {
|
||||
fmt.Fprint(me.outf, string(p))
|
||||
}
|
||||
|
||||
if w == nil {
|
||||
me.stdout.outputS = append(me.stdout.outputS, lines...)
|
||||
return len(p), nil
|
||||
}
|
||||
w.tainted = true
|
||||
me.writeMutex.Lock()
|
||||
defer me.writeMutex.Unlock()
|
||||
|
||||
me.stdout.outputS = append(me.stdout.outputS, lines...)
|
||||
|
||||
tk := me.stdout.tk
|
||||
if tk == nil {
|
||||
return len(p), nil
|
||||
}
|
||||
if tk.v == nil {
|
||||
// redo this old code
|
||||
v, _ := me.baseGui.View("msg")
|
||||
if v != nil {
|
||||
tk.v = v
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
tk.refreshStdout()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// lets the user page up and down through the stdout buffer
|
||||
func (tk *guiWidget) refreshStdout() {
|
||||
if len(me.stdout.outputS) < me.stdout.h+me.stdout.pager {
|
||||
// log.Info(fmt.Sprintf("buffer too small=%d len(%d)", me.stdout.pager, len(me.stdout.outputS)))
|
||||
var cur []string
|
||||
cur = append(cur, me.stdout.outputS...)
|
||||
slices.Reverse(cur)
|
||||
tk.v.Clear()
|
||||
fmt.Fprintln(tk.v, strings.Join(cur, "\n"))
|
||||
return
|
||||
}
|
||||
|
||||
var cur []string
|
||||
// chop off the last lines in the buffer
|
||||
chop := len(me.stdout.outputS) - (me.stdout.pager + me.stdout.h)
|
||||
cur = append(cur, me.stdout.outputS[chop:chop+me.stdout.h]...)
|
||||
if me.stdout.reverse {
|
||||
slices.Reverse(cur)
|
||||
}
|
||||
tk.v.Clear()
|
||||
fmt.Fprintln(tk.v, strings.Join(cur, "\n"))
|
||||
}
|
336
structs.go
336
structs.go
|
@ -1,5 +1,5 @@
|
|||
// LICENSE: same as the go language itself
|
||||
// Copyright 2023 WIT.COM
|
||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
// all structures and variables are local (aka lowercase)
|
||||
// since the plugin should be isolated to access only
|
||||
|
@ -10,94 +10,150 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.wit.com/log"
|
||||
"github.com/awesome-gocui/gocui"
|
||||
|
||||
"go.wit.com/lib/protobuf/guipb"
|
||||
log "go.wit.com/log"
|
||||
"go.wit.com/toolkits/tree"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
var initOnce sync.Once // run initPlugin() only once
|
||||
|
||||
// It's probably a terrible idea to call this 'me'
|
||||
// 2025 note: doesn't seem terrible to call this 'me' anymore. notsure.
|
||||
var me config
|
||||
|
||||
var showDebug bool = true
|
||||
var showHelp bool = true
|
||||
var redoWidgets bool = true
|
||||
|
||||
// This is the window that is currently active
|
||||
var currentWindow *node
|
||||
|
||||
// todo: move all this to a protobuf. then, redo all this mess.
|
||||
// it got me here, but now it's time to clean it up for good
|
||||
// I can't get a GO plugins that use protobuf to load yet (versioning mismatch)
|
||||
type config struct {
|
||||
baseGui *gocui.Gui // the main gocui handle
|
||||
rootNode *node // the base of the binary tree. it should have id == 0
|
||||
|
||||
ctrlDown *node // shown if you click the mouse when the ctrl key is pressed
|
||||
currentWindow *node // this is the current tab or window to show
|
||||
logStdout *node // where to show STDOUT
|
||||
helpLabel *gocui.View
|
||||
ddview *node // the gocui view to select dropdrown lists
|
||||
ddClicked bool // the dropdown menu view was clicked
|
||||
ddNode *node // the dropdown menu is for this widget
|
||||
|
||||
/*
|
||||
// this is the channel we send user events like
|
||||
// mouse clicks or keyboard events back to the program
|
||||
callback chan toolkit.Action
|
||||
|
||||
// this is the channel we get requests to make widgets
|
||||
pluginChan chan toolkit.Action
|
||||
*/
|
||||
|
||||
// When the widget has a frame, like a button, it adds 2 lines runes on each side
|
||||
// so you need 3 char spacing in each direction to not have them overlap
|
||||
// the amount of padding when there is a frame
|
||||
FramePadW int `default:"1" dense:"0"`
|
||||
FramePadH int `default:"1" dense:"0"`
|
||||
|
||||
PadW int `default:"1" dense:"0"`
|
||||
PadH int `default:"1" dense:"0"`
|
||||
|
||||
// how far down to start Window or Tab headings
|
||||
WindowW int `default:"8" dense:"0"`
|
||||
WindowH int `default:"-1"`
|
||||
TabW int `default:"5" dense:"0"`
|
||||
TabH int `default:"1" dense:"0"`
|
||||
|
||||
// additional amount of space to put between window & tab widgets
|
||||
WindowPadW int `default:"8" dense:"0"`
|
||||
TabPadW int `default:"4" dense:"0"`
|
||||
|
||||
// additional amount of space to indent on a group
|
||||
GroupPadW int `default:"6" dense:"2"`
|
||||
|
||||
// the raw beginning of each window (or tab)
|
||||
RawW int `default:"1"`
|
||||
RawH int `default:"5"`
|
||||
|
||||
// offset for the hidden widgets
|
||||
FakeW int `default:"20"`
|
||||
|
||||
treeRoot *tree.Node // the base of the binary tree. it should have id == 0
|
||||
myTree *tree.TreeInfo // ?
|
||||
currentWindow *guiWidget // this is the current tab or window to show
|
||||
ok bool // if the user doesn't hit a key or move the mouse, gocui doesn't really start
|
||||
firstWindowOk bool // allows the init to wait for the first window from the application
|
||||
refresh bool // redraw everything?
|
||||
ctrlDown *tree.Node // shown if you click the mouse when the ctrl key is pressed
|
||||
helpLabel *gocui.View // ?
|
||||
showHelp bool // toggle boolean for the help menu (deprecate?)
|
||||
FirstWindowW int `default:"2"` // how far over to start window #1
|
||||
FirstWindowH int `default:"0"` // how far down to start window #1
|
||||
FramePadW int `default:"1" dense:"0"` // When the widget has a frame, like a button, it adds 2 lines runes on each side
|
||||
FramePadH int `default:"1" dense:"0"` // When the widget has a frame, like a button, it adds 2 lines runes on each side
|
||||
PadW int `default:"1" dense:"0"` // pad spacing
|
||||
PadH int `default:"1" dense:"0"` // pad spacing
|
||||
WindowW int `default:"8" dense:"0"` // how far down to start Window or Tab headings
|
||||
WindowH int `default:"-1"` // how far down to start Window or Tab headings
|
||||
TabW int `default:"5" dense:"0"` // how far down to start Window or Tab headings
|
||||
TabH int `default:"1" dense:"0"` // how far down to start Window or Tab headings
|
||||
WindowPadW int `default:"8" dense:"0"` // additional amount of space to put between window & tab widgets
|
||||
TabPadW int `default:"4" dense:"0"` // additional amount of space to put between window & tab widgets
|
||||
GroupPadW int `default:"2" dense:"1"` // additional amount of space to indent on a group
|
||||
BoxPadW int `default:"2" dense:"1"` // additional amount of space to indent on a box
|
||||
GridPadW int `default:"2" dense:"1"` // additional amount of space to indent on a grid
|
||||
RawW int `default:"1"` // the raw beginning of each window (or tab)
|
||||
RawH int `default:"5"` // the raw beginning of each window (or tab)
|
||||
FakeW int `default:"20"` // offset for the hidden widgets
|
||||
DropdownId int `default:"-78"` // the widget id to use
|
||||
padded bool // add space between things like buttons
|
||||
bookshelf bool // do you want things arranged in the box like a bookshelf or a stack?
|
||||
canvas bool // if set to true, the windows are a raw canvas
|
||||
menubar bool // for windows
|
||||
stretchy bool // expand things like buttons to the maximum size
|
||||
margin bool // add space around the frames of windows
|
||||
|
||||
// writeMutex protects locks the write process
|
||||
writeMutex sync.Mutex
|
||||
|
||||
// used for listWidgets() debugging
|
||||
depth int
|
||||
writeMutex sync.Mutex // writeMutex protects writes to *guiWidget (it's global right now maybe)
|
||||
ecount int // counts how many mouse and keyboard events have occurred
|
||||
supermouse bool // prints out every widget found while you move the mouse around
|
||||
depth int // used for listWidgets() debugging
|
||||
newWindowTrigger chan *guiWidget // work around hack to redraw windows a bit after NewWindow()
|
||||
stdout stdout // information for the STDOUT window
|
||||
dropdown dropdown // the dropdown menu
|
||||
textbox dropdown // the textbox popup window
|
||||
BG dropdown // the background widget
|
||||
notify libnotify // emulates the desktop libnotify menu
|
||||
allwin []*guiWidget // for tracking which window is next
|
||||
dark bool // use a 'dark' color palette
|
||||
mouse mouse // mouse settings
|
||||
showDebug bool // todo: move this into config struct
|
||||
debug bool // todo: move this into config struct
|
||||
starttime time.Time // checks how long it takes on startup
|
||||
winchW int // used to detect SIGWINCH
|
||||
winchH int // used to detect SIGWINCH
|
||||
outf *os.File // hacks for capturing stdout
|
||||
}
|
||||
|
||||
// deprecate these
|
||||
var (
|
||||
initialMouseX, initialMouseY, xOffset, yOffset int
|
||||
globalMouseDown, msgMouseDown, movingMsg bool
|
||||
)
|
||||
// stuff controlling how the mouse works
|
||||
type mouse struct {
|
||||
down time.Time // when the mouse was pressed down
|
||||
up time.Time // when the mouse was released. used to detect click vs drag
|
||||
clicktime time.Duration // how long is too long for a mouse click vs drag
|
||||
mouseUp bool // is the mouse up?
|
||||
double bool // user is double clicking
|
||||
doubletime time.Duration // how long is too long for double click
|
||||
resize bool // mouse is resizing something
|
||||
downW int // where the mouse was pressed down
|
||||
downH int // where the mouse was pressed down
|
||||
currentDrag *guiWidget // what widget is currently being moved around
|
||||
}
|
||||
|
||||
// settings for the stdout window
|
||||
type stdout struct {
|
||||
tk *guiWidget // where to show STDOUT
|
||||
wId int // the widget id
|
||||
w int // the width
|
||||
h int // the height
|
||||
outputOnTop bool // is the STDOUT window on top?
|
||||
outputOffscreen bool // is the STDOUT window offscreen?
|
||||
startOnscreen bool // start the output window onscreen?
|
||||
disable bool // disable the stdout window. do not change os.Stdout & os.Stderr
|
||||
lastW int // the last 'w' location (used to move from offscreen to onscreen)
|
||||
lastH int // the last 'h' location (used to move from offscreen to onscreen)
|
||||
init bool // moves the window offscreen on startup
|
||||
outputS []string // the buffer of all the output
|
||||
pager int // allows the user to page through the buffer
|
||||
changed bool // indicates the user has changed stdout. gocui should remember the state here
|
||||
reverse bool // flip the STDOUT upside down so new STDOUT lines are at the top
|
||||
}
|
||||
|
||||
// settings for the dropdown window
|
||||
type dropdown struct {
|
||||
tk *guiWidget // where to show STDOUT
|
||||
callerTK *guiWidget // which widget called the dropdown menu
|
||||
items []string // what is currently in the menu
|
||||
w int // the width
|
||||
h int // the height
|
||||
active bool // is the dropdown menu currently in use?
|
||||
init bool // moves the window offscreen on startup
|
||||
// Id int `default:"-78"` // the widget id to use
|
||||
wId int `default:"-78"` // the widget id to use
|
||||
}
|
||||
|
||||
// settings for the dropdown window
|
||||
type internalTK struct {
|
||||
once sync.Once // for init
|
||||
tk *guiWidget // where to show STDOUT
|
||||
callerTK *guiWidget // which widget called the dropdown menu
|
||||
wId int // the widget id to use
|
||||
active bool // is the internal widget currently in use?
|
||||
offsetW int // width offset
|
||||
offsetH int // height offset
|
||||
}
|
||||
|
||||
// the desktop libnotify menu
|
||||
type libnotify struct {
|
||||
clock internalTK // widget for the clock
|
||||
icon internalTK // libnotify menu icon
|
||||
window internalTK // the libnotify menu
|
||||
help internalTK // the help menu
|
||||
}
|
||||
|
||||
// this is the gocui way
|
||||
// corner starts at in the upper left corner
|
||||
|
@ -110,80 +166,87 @@ func (r *rectType) Width() int {
|
|||
}
|
||||
|
||||
func (r *rectType) Height() int {
|
||||
if r.h0 == 0 && r.h1 == 0 {
|
||||
// edge case. only return 0 for these
|
||||
return 0
|
||||
}
|
||||
if r.h1 == r.h0 {
|
||||
// if they are equal, it's actually height = 1
|
||||
return 1
|
||||
}
|
||||
if r.h1-r.h0 < 1 {
|
||||
// can't have negatives. something is wrong. return 1 for now
|
||||
return 1
|
||||
}
|
||||
|
||||
return r.h1 - r.h0
|
||||
}
|
||||
|
||||
// settings that are window specific
|
||||
type window struct {
|
||||
windowFrame *guiWidget // this is the frame for a window widget
|
||||
wasDragged bool // indicates the window was dragged. This keeps it from being rearranged
|
||||
hasTabs bool // does the window have tabs?
|
||||
currentTab bool // the visible tab
|
||||
selectedTab *tree.Node // for a window, this is currently selected tab
|
||||
active bool // means this window is the active one
|
||||
order int // what level the window is on
|
||||
// resize bool // only set the title once
|
||||
collapsed bool // only show the window title bar
|
||||
dense bool // true if the window is dense
|
||||
large bool // true if the window is huge
|
||||
pager int // allows the user to page through the window
|
||||
}
|
||||
|
||||
type colorT struct {
|
||||
frame gocui.Attribute
|
||||
fg gocui.Attribute
|
||||
bg gocui.Attribute
|
||||
selFg gocui.Attribute
|
||||
selBg gocui.Attribute
|
||||
name string
|
||||
}
|
||||
|
||||
type guiWidget struct {
|
||||
// the gocui package variables
|
||||
v *gocui.View // this is nil if the widget is not displayed
|
||||
cuiName string // what gocui uses to reference the widget
|
||||
|
||||
// the actual text to display in the console
|
||||
label string
|
||||
|
||||
// the logical size of the widget
|
||||
// For example, 40x12 would be the center of a normal terminal
|
||||
// size rectType
|
||||
|
||||
// the actual gocui display view of this widget
|
||||
// sometimes this isn't visible like with a Box or Grid
|
||||
gocuiSize rectType
|
||||
|
||||
isCurrent bool // is this the currently displayed Window or Tab?
|
||||
cuiName string // what gocui uses to reference the widget (usually "TK <widgetId>"
|
||||
parent *guiWidget // mirrors the binary node tree
|
||||
children []*guiWidget // mirrors the binary node tree
|
||||
node *tree.Node // the pointer back to the tree
|
||||
pb *guipb.Widget // the guipb Widget
|
||||
wtype widget.WidgetType // used for Tables for now. todo: fix this correctly
|
||||
windowFrame *guiWidget // this is the frame for a window widget
|
||||
internal bool // indicates the widget is internal to gocui and should be treated differently
|
||||
hasTabs bool // does the window have tabs?
|
||||
currentTab bool // the visible tab
|
||||
window window // holds information specific only to Window widgets
|
||||
value string // ?
|
||||
checked bool // ?
|
||||
labelN string // the actual text to display in the console
|
||||
vals []string // dropdown menu items
|
||||
enable bool // ?
|
||||
gocuiSize rectType // should mirror the real display size. todo: verify these are accurate. they are not yet
|
||||
full rectType // full size of children (used by widget.Window, etc)
|
||||
force rectType // force widget within these boundries (using this to debug window dragging)
|
||||
startW int // ?
|
||||
startH int // ?
|
||||
lastW int // used during mouse dragging
|
||||
lastH int // used during mouse dragging
|
||||
isFake bool // widget types like 'box' are 'false'
|
||||
|
||||
// used to track the size of grids
|
||||
widths map[int]int // how tall each row in the grid is
|
||||
heights map[int]int // how wide each column in the grid is
|
||||
|
||||
tainted bool
|
||||
frame bool
|
||||
|
||||
// for a window, this is currently selected tab
|
||||
selectedTab *node
|
||||
|
||||
// what color to use
|
||||
color *colorT
|
||||
}
|
||||
|
||||
// from the gocui devs:
|
||||
// Write appends a byte slice into the view's internal buffer. Because
|
||||
// View implements the io.Writer interface, it can be passed as parameter
|
||||
// of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must
|
||||
// be called to clear the view's buffer.
|
||||
|
||||
func (w *guiWidget) Write(p []byte) (n int, err error) {
|
||||
w.tainted = true
|
||||
me.writeMutex.Lock()
|
||||
defer me.writeMutex.Unlock()
|
||||
if me.logStdout.tk.v == nil {
|
||||
// optionally write the output to /tmp
|
||||
s := fmt.Sprint(string(p))
|
||||
s = strings.TrimSuffix(s, "\n")
|
||||
fmt.Fprintln(outf, s)
|
||||
v, _ := me.baseGui.View("msg")
|
||||
if v != nil {
|
||||
// fmt.Fprintln(outf, "found msg")
|
||||
me.logStdout.tk.v = v
|
||||
}
|
||||
} else {
|
||||
// display the output in the gocui window
|
||||
me.logStdout.tk.v.Clear()
|
||||
|
||||
s := fmt.Sprint(string(p))
|
||||
s = strings.TrimSuffix(s, "\n")
|
||||
tmp := strings.Split(s, "\n")
|
||||
outputS = append(outputS, tmp...)
|
||||
if len(outputS) > outputH {
|
||||
l := len(outputS) - outputH
|
||||
outputS = outputS[l:]
|
||||
}
|
||||
fmt.Fprintln(me.logStdout.tk.v, strings.Join(outputS, "\n"))
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
tainted bool // ?
|
||||
frame bool // ?
|
||||
selectedTab *tree.Node // for a window, this is currently selected tab
|
||||
color *colorT // what color to use
|
||||
colorLast colorT // the last color the widget had
|
||||
defaultColor *colorT // the default colors // TODO: make a function for this instead
|
||||
isTable bool // is this a table?
|
||||
}
|
||||
|
||||
// THIS IS GO COMPILER MAGIC
|
||||
// this sets the `default` in the structs above on init()
|
||||
// this is cool code. thank the GO devs for this code and Alex Flint for explaining it to me
|
||||
func Set(ptr interface{}, tag string) error {
|
||||
if reflect.TypeOf(ptr).Kind() != reflect.Ptr {
|
||||
log.Log(ERROR, "Set() Not a pointer", ptr, "with tag =", tag)
|
||||
|
@ -203,12 +266,11 @@ func Set(ptr interface{}, tag string) error {
|
|||
}
|
||||
|
||||
func setField(field reflect.Value, defaultVal string, name string) error {
|
||||
|
||||
if !field.CanSet() {
|
||||
// log("setField() Can't set value", field, defaultVal)
|
||||
return fmt.Errorf("Can't set value\n")
|
||||
} else {
|
||||
log.Log(NOW, "setField() Can set value", name, defaultVal)
|
||||
// log.Log(NOW, "setField() Can set value", name, defaultVal)
|
||||
}
|
||||
|
||||
switch field.Kind() {
|
||||
|
|
111
tab.go
111
tab.go
|
@ -1,111 +0,0 @@
|
|||
package main
|
||||
|
||||
// implements widgets 'Window' and 'Tab'
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func (w *guiWidget) Width() int {
|
||||
if w.frame {
|
||||
return w.gocuiSize.w1 - w.gocuiSize.w0
|
||||
}
|
||||
return w.gocuiSize.w1 - w.gocuiSize.w0 - 1
|
||||
}
|
||||
|
||||
func (w *guiWidget) Height() int {
|
||||
if w.frame {
|
||||
return w.gocuiSize.h1 - w.gocuiSize.h0
|
||||
}
|
||||
return w.gocuiSize.h1 - w.gocuiSize.h0 - 1
|
||||
}
|
||||
|
||||
func (n *node) gocuiSetWH(sizeW, sizeH int) {
|
||||
w := len(widget.GetString(n.value))
|
||||
lines := strings.Split(widget.GetString(n.value), "\n")
|
||||
h := len(lines)
|
||||
|
||||
tk := n.tk
|
||||
if tk.isFake {
|
||||
tk.gocuiSize.w0 = sizeW
|
||||
tk.gocuiSize.h0 = sizeH
|
||||
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW
|
||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH
|
||||
return
|
||||
}
|
||||
|
||||
if tk.frame {
|
||||
tk.gocuiSize.w0 = sizeW
|
||||
tk.gocuiSize.h0 = sizeH
|
||||
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW
|
||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH
|
||||
} else {
|
||||
tk.gocuiSize.w0 = sizeW - 1
|
||||
tk.gocuiSize.h0 = sizeH - 1
|
||||
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + 1
|
||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + 1
|
||||
}
|
||||
}
|
||||
|
||||
func redoWindows(nextW int, nextH int) {
|
||||
for _, n := range me.rootNode.children {
|
||||
if n.WidgetType != widget.Window {
|
||||
continue
|
||||
}
|
||||
w := n.tk
|
||||
var tabs bool
|
||||
for _, child := range n.children {
|
||||
if child.WidgetType == widget.Tab {
|
||||
tabs = true
|
||||
}
|
||||
}
|
||||
if tabs {
|
||||
// window is tabs. Don't show it as a standard button
|
||||
w.frame = false
|
||||
n.hasTabs = true
|
||||
} else {
|
||||
w.frame = false
|
||||
n.hasTabs = false
|
||||
}
|
||||
|
||||
n.gocuiSetWH(nextW, nextH)
|
||||
n.deleteView()
|
||||
n.showView()
|
||||
|
||||
sizeW := w.Width() + me.WindowPadW
|
||||
sizeH := w.Height()
|
||||
nextW += sizeW
|
||||
log.Log(NOW, "redoWindows() start nextW,H =", nextW, nextH, "gocuiSize.W,H =", sizeW, sizeH, n.progname)
|
||||
|
||||
if n.hasTabs {
|
||||
n.redoTabs(me.TabW, me.TabH)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *node) redoTabs(nextW int, nextH int) {
|
||||
for _, n := range p.children {
|
||||
if n.WidgetType != widget.Tab {
|
||||
continue
|
||||
}
|
||||
w := n.tk
|
||||
w.frame = true
|
||||
|
||||
n.gocuiSetWH(nextW, nextH)
|
||||
n.deleteView()
|
||||
// setCurrentTab(n)
|
||||
// if (len(w.cuiName) < 4) {
|
||||
// w.cuiName = "abcd"
|
||||
// }
|
||||
|
||||
n.showView()
|
||||
|
||||
sizeW := w.Width() + me.TabPadW
|
||||
sizeH := w.Height()
|
||||
log.Log(NOW, "redoTabs() start nextW,H =", nextW, nextH, "gocuiSize.W,H =", sizeW, sizeH, n.progname)
|
||||
nextW += sizeW
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"go.wit.com/lib/protobuf/guipb"
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/toolkits/tree"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func initGridPB(pb *guipb.Widget) *guiWidget {
|
||||
var w *guiWidget
|
||||
w = new(guiWidget)
|
||||
|
||||
w.pb = pb
|
||||
w.wtype = widget.Grid
|
||||
w.cuiName = fmt.Sprintf("%d %s", pb.Id, "TK")
|
||||
w.labelN = pb.Name
|
||||
w.isTable = true
|
||||
return w
|
||||
}
|
||||
|
||||
func showTable(t *guipb.Table) {
|
||||
log.Info("gocui: should show table here")
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
log.Info("gocui: table.Title", t.Title)
|
||||
// log.Info("gocui: need to add window here id =", t.Window.Id, t.Window.Name)
|
||||
if t.Grid == nil {
|
||||
log.Info("gocui: missing grid widget. tree plugin error")
|
||||
return
|
||||
}
|
||||
root := me.treeRoot.TK.(*guiWidget)
|
||||
parent := root.findWidgetById(int(t.Parent.Id))
|
||||
if parent == nil {
|
||||
log.Info("gocui: show table error. parent.Id not found", t.Parent.Id)
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("gocui: need to add grid here id =", t.Grid.Id)
|
||||
grid := initGridPB(t.Grid)
|
||||
grid.parent = parent
|
||||
}
|
||||
|
||||
func enableWidget(n *tree.Node) {
|
||||
tk := n.TK.(*guiWidget)
|
||||
tk.Enable()
|
||||
}
|
||||
|
||||
func disableWidget(n *tree.Node) {
|
||||
tk := n.TK.(*guiWidget)
|
||||
tk.Disable()
|
||||
}
|
||||
|
||||
func showWidget(n *tree.Node) {
|
||||
tk := n.TK.(*guiWidget)
|
||||
tk.Show()
|
||||
}
|
||||
|
||||
func hideWidget(n *tree.Node) {
|
||||
tk := n.TK.(*guiWidget)
|
||||
if n.WidgetType == widget.Window {
|
||||
tk.windowFrame.Hide()
|
||||
tk.hideWidgets()
|
||||
}
|
||||
tk.Hide()
|
||||
tk.deleteWidget()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) deleteWidget() {
|
||||
log.Log(INFO, "gocui deleteWidget() looking for child to delete:", tk.cuiName)
|
||||
p := tk.parent
|
||||
for i, child := range p.children {
|
||||
if tk == child {
|
||||
log.Log(INFO, "deleteWidget() found parent with child to delete:", i, child.cuiName, child.WidgetId())
|
||||
p.children = slices.Delete(p.children, i, i+1)
|
||||
}
|
||||
}
|
||||
tk.deleteTree()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) deleteTree() {
|
||||
for _, child := range tk.children {
|
||||
child.deleteTree()
|
||||
}
|
||||
tk.Hide()
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
// simulates a dropdown menu in gocui
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
log "go.wit.com/log"
|
||||
)
|
||||
|
||||
func (tk *guiWidget) forceSizes(r *rectType) {
|
||||
tk.gocuiSize.w0 = r.w0
|
||||
tk.gocuiSize.w1 = r.w1
|
||||
tk.gocuiSize.h0 = r.h0
|
||||
tk.gocuiSize.h1 = r.h1
|
||||
|
||||
tk.full.w0 = r.w0
|
||||
tk.full.w1 = r.w1
|
||||
tk.full.h0 = r.h0
|
||||
tk.full.h1 = r.h1
|
||||
|
||||
tk.force.w0 = r.w0
|
||||
tk.force.w1 = r.w1
|
||||
tk.force.h0 = r.h0
|
||||
tk.force.h1 = r.h1
|
||||
}
|
||||
|
||||
func initTextbox() {
|
||||
if me.textbox.tk == nil {
|
||||
// should only happen once
|
||||
me.textbox.tk = makeNewFlagWidget(me.textbox.wId)
|
||||
// me.textbox.tk.dumpWidget("init() textbox")
|
||||
}
|
||||
}
|
||||
|
||||
func (callertk *guiWidget) prepTextbox() {
|
||||
initTextbox()
|
||||
if me.textbox.tk == nil {
|
||||
log.Log(WARN, "prepTextbox() Is Broken")
|
||||
return
|
||||
}
|
||||
|
||||
r := new(rectType)
|
||||
// startW, startH := tk.Position()
|
||||
r.w0 = callertk.gocuiSize.w0 + 4
|
||||
r.h0 = callertk.gocuiSize.h0 + 3
|
||||
r.w1 = r.w0 + 24
|
||||
r.h1 = r.h0 + 2
|
||||
me.textbox.tk.forceSizes(r)
|
||||
me.textbox.tk.dumpWidget("after sizes")
|
||||
|
||||
me.textbox.callerTK = callertk
|
||||
|
||||
if me.textbox.tk.v != nil {
|
||||
log.Log(WARN, "WARNING textbox DeleteView()")
|
||||
log.Log(WARN, "WARNING textbox DeleteView()")
|
||||
log.Log(WARN, "WARNING textbox DeleteView()")
|
||||
me.baseGui.DeleteView(me.textbox.tk.cuiName)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
if err := me.textbox.tk.SetViewRect(r); err != nil {
|
||||
log.Log(WARN, "textbox SetViewRect() failed", err, "view name =", me.textbox.tk.cuiName)
|
||||
return
|
||||
}
|
||||
// me.textbox.tk.Show() // actually makes the gocui view. TODO: redo this?
|
||||
showTextbox(callertk.String())
|
||||
}
|
||||
|
||||
func showTextbox(callers string) {
|
||||
// tk := me.textbox.tk
|
||||
// me.textbox.tk.dumpWidget("after sizes")
|
||||
log.Log(WARN, "showTextbox() caller string =", callers)
|
||||
|
||||
// me.textbox.tk.Show() // actually makes the gocui view. TODO: redo this
|
||||
|
||||
if me.textbox.tk.v == nil {
|
||||
log.Log(WARN, "textbox.tk.v == nil showTextbox() is broken")
|
||||
return
|
||||
}
|
||||
|
||||
me.textbox.tk.setColorModal()
|
||||
me.textbox.tk.v.Clear()
|
||||
cur := strings.TrimSpace(callers)
|
||||
// log.Info("setting textbox string to:", cur)
|
||||
me.textbox.tk.v.WriteString(cur)
|
||||
|
||||
me.textbox.tk.v.Editable = true
|
||||
me.textbox.tk.v.Wrap = true
|
||||
|
||||
me.textbox.tk.SetView()
|
||||
|
||||
me.baseGui.SetCurrentView(me.textbox.tk.v.Name())
|
||||
|
||||
// bind the enter key to a function so we can close the textbox
|
||||
me.baseGui.SetKeybinding(me.textbox.tk.v.Name(), gocui.KeyEnter, gocui.ModNone, theCloseTheTextbox)
|
||||
|
||||
me.textbox.active = true
|
||||
|
||||
me.baseGui.SetViewOnTop(me.textbox.tk.v.Name())
|
||||
me.textbox.tk.dumpWidget("showTextbox()")
|
||||
}
|
||||
|
||||
func theCloseTheTextbox(g *gocui.Gui, v *gocui.View) error {
|
||||
textboxClosed()
|
||||
return nil
|
||||
}
|
||||
|
||||
// updates the text and sends an event back to the application
|
||||
func textboxClosed() {
|
||||
// get the text the user entered
|
||||
var newtext string
|
||||
if me.textbox.tk.v == nil {
|
||||
newtext = ""
|
||||
} else {
|
||||
newtext = me.textbox.tk.v.ViewBuffer()
|
||||
}
|
||||
newtext = strings.TrimSpace(newtext)
|
||||
me.textbox.active = false
|
||||
me.textbox.tk.Hide()
|
||||
// log.Info("textbox closed with text:", newtext, me.textbox.callerTK.cuiName)
|
||||
|
||||
if me.notify.clock.tk.v != nil {
|
||||
me.baseGui.SetCurrentView("help")
|
||||
} else {
|
||||
me.baseGui.SetCurrentView("msg")
|
||||
}
|
||||
|
||||
// change the text of the caller widget
|
||||
me.textbox.callerTK.SetText(newtext)
|
||||
me.textbox.callerTK.node.SetCurrentS(newtext)
|
||||
|
||||
// send an event from the plugin with the new string
|
||||
me.myTree.SendUserEvent(me.textbox.callerTK.node)
|
||||
|
||||
win := me.textbox.callerTK.findParentWindow()
|
||||
if win != nil {
|
||||
// win.dumpWidget("redraw this!!!")
|
||||
tk := me.textbox.callerTK
|
||||
// me.textbox.callerTK.dumpWidget("resize this!!!")
|
||||
me.textbox.callerTK.Size()
|
||||
me.textbox.callerTK.placeWidgets(tk.gocuiSize.w0-4, tk.gocuiSize.h0-4)
|
||||
// tk.dumpWidget("resize:" + tk.String())
|
||||
win.makeWindowActive()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
/*
|
||||
DO NOT EDIT THIS FILE
|
||||
|
||||
this file is the same for every GUI toolkit plugin
|
||||
when you are making a new GUI toolkit plugin for
|
||||
a specific toolkit, you just need to define these
|
||||
functions.
|
||||
|
||||
for example, in the "gocui" toolkit, the functions
|
||||
below are what triggers the "gocui" GO package
|
||||
to draw labels, buttons, windows, etc
|
||||
|
||||
If you are starting out trying to make a new GUI toolkit,
|
||||
all you have to do is copy this file over. Then
|
||||
work on making these functions. addWidget(), setText(), etc.
|
||||
|
||||
That's it!
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
/*
|
||||
This is reference code for toolkit developers
|
||||
|
||||
This is how information is passed in GO back to the application
|
||||
via the GO 'plugin' concept
|
||||
|
||||
TODO: switch this to protocol buffers
|
||||
*/
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
log "go.wit.com/log"
|
||||
"go.wit.com/toolkits/tree"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
// Other goroutines must use this to access the GUI
|
||||
//
|
||||
// You can not acess / process the GUI thread directly from
|
||||
// other goroutines. This is due to the nature of how
|
||||
// Linux, MacOS and Windows work (they all work differently. suprise. surprise.)
|
||||
//
|
||||
// this sets the channel to send user events back from the plugin
|
||||
func Callback(guiCallback chan widget.Action) {
|
||||
me.myTree.Callback(guiCallback)
|
||||
}
|
||||
|
||||
func PluginChannel() chan widget.Action {
|
||||
initOnce.Do(initPlugin)
|
||||
for {
|
||||
if me.myTree != nil {
|
||||
break
|
||||
}
|
||||
log.Info("me.myTree == nil")
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
return me.myTree.PluginChannel()
|
||||
}
|
||||
|
||||
func FrozenChannel() chan widget.Action {
|
||||
return me.myTree.FrozenChannel()
|
||||
}
|
||||
|
||||
func initTree() *tree.TreeInfo {
|
||||
t := tree.New()
|
||||
t.PluginName = PLUGIN
|
||||
t.Add = newAdd
|
||||
t.SetTitle = setTitle
|
||||
t.SetLabel = setLabel
|
||||
t.SetText = setText
|
||||
t.AddText = addText
|
||||
|
||||
t.Enable = enableWidget
|
||||
t.Disable = disableWidget
|
||||
|
||||
t.Show = showWidget
|
||||
t.Hide = hideWidget
|
||||
|
||||
t.SetChecked = setChecked
|
||||
t.ToolkitInit = toolkitInit
|
||||
t.ToolkitClose = toolkitClose
|
||||
t.ShowTable = showTable
|
||||
|
||||
return t
|
||||
}
|
247
view.go
247
view.go
|
@ -1,233 +1,106 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func splitLines(s string) []string {
|
||||
var lines []string
|
||||
sc := bufio.NewScanner(strings.NewReader(s))
|
||||
for sc.Scan() {
|
||||
lines = append(lines, sc.Text())
|
||||
// expands the gocuiSize rectangle to fit
|
||||
// all the text in tk.labelN
|
||||
func (tk *guiWidget) textResize() {
|
||||
var w, h int = 0, 0
|
||||
|
||||
for _, s := range strings.Split(tk.labelN, "\n") {
|
||||
s = strings.TrimSpace(s)
|
||||
// log.Log(INFO, "textResize() len =", len(s), i, s)
|
||||
if w < len(s) {
|
||||
w = len(s)
|
||||
}
|
||||
return lines
|
||||
h += 1
|
||||
}
|
||||
|
||||
func (n *node) textResize() bool {
|
||||
w := n.tk
|
||||
var width, height int = 0, 0
|
||||
var changed bool = false
|
||||
|
||||
for i, s := range splitLines(n.tk.label) {
|
||||
log.Log(INFO, "textResize() len =", len(s), i, s)
|
||||
if width < len(s) {
|
||||
width = len(s)
|
||||
// todo: fix all this old code
|
||||
if tk.WidgetType() == widget.Textbox {
|
||||
if w < 5 {
|
||||
w = 5
|
||||
}
|
||||
height += 1
|
||||
}
|
||||
if w.gocuiSize.w1 != w.gocuiSize.w0+width+me.FramePadW {
|
||||
w.gocuiSize.w1 = w.gocuiSize.w0 + width + me.FramePadW
|
||||
changed = true
|
||||
}
|
||||
if w.gocuiSize.h1 != w.gocuiSize.h0+height+me.FramePadH {
|
||||
w.gocuiSize.h1 = w.gocuiSize.h0 + height + me.FramePadH
|
||||
changed = true
|
||||
}
|
||||
if changed {
|
||||
n.showWidgetPlacement(true, "textResize() changed")
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
func (n *node) hideView() {
|
||||
n.SetVisible(false)
|
||||
// this is old code. now move this somewhere smarter
|
||||
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW // TODO: move this FramePadW out of here
|
||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH // TODO: fix this size computation
|
||||
}
|
||||
|
||||
// display's the text of the widget in gocui
|
||||
// will create a new gocui view if there isn't one or if it has been moved
|
||||
func (n *node) showView() {
|
||||
var err error
|
||||
w := n.tk
|
||||
|
||||
if w.cuiName == "" {
|
||||
log.Log(ERROR, "showView() w.cuiName was not set for widget", w)
|
||||
w.cuiName = string(n.WidgetId)
|
||||
}
|
||||
|
||||
// if the gocui element doesn't exist, create it
|
||||
if w.v == nil {
|
||||
n.recreateView()
|
||||
}
|
||||
x0, y0, x1, y1, err := me.baseGui.ViewPosition(w.cuiName)
|
||||
log.Log(INFO, "showView() w.v already defined for widget", n.progname, err)
|
||||
|
||||
// n.smartGocuiSize()
|
||||
changed := n.textResize()
|
||||
|
||||
if changed {
|
||||
log.Log(NOW, "showView() textResize() changed. Should recreateView here wId =", w.cuiName)
|
||||
} else {
|
||||
log.Log(NOW, "showView() Clear() and Fprint() here wId =", w.cuiName)
|
||||
w.v.Clear()
|
||||
fmt.Fprint(w.v, n.tk.label)
|
||||
n.SetVisible(false)
|
||||
n.SetVisible(true)
|
||||
// deletes every view
|
||||
func (w *guiWidget) hideWindow() {
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
w.Hide()
|
||||
for _, child := range w.children {
|
||||
child.hideWindow()
|
||||
}
|
||||
}
|
||||
|
||||
// if the gocui element has changed where it is supposed to be on the screen
|
||||
// recreate it
|
||||
if x0 != w.gocuiSize.w0 {
|
||||
n.recreateView()
|
||||
func (w *guiWidget) hideWidgets() {
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
if y0 != w.gocuiSize.h0 {
|
||||
log.Log(ERROR, "showView() start hight mismatch id=", w.cuiName, "gocui h vs computed h =", w.gocuiSize.h0, y0)
|
||||
n.recreateView()
|
||||
return
|
||||
}
|
||||
if x1 != w.gocuiSize.w1 {
|
||||
log.Log(ERROR, "showView() too wide", w.cuiName, "w,w", w.gocuiSize.w1, x1)
|
||||
n.recreateView()
|
||||
return
|
||||
}
|
||||
if y1 != w.gocuiSize.h1 {
|
||||
log.Log(ERROR, "showView() too high", w.cuiName, "h,h", w.gocuiSize.h1, y1)
|
||||
n.recreateView()
|
||||
return
|
||||
}
|
||||
|
||||
n.SetVisible(true)
|
||||
}
|
||||
|
||||
// create or recreate the gocui widget visible
|
||||
// deletes the old view if it exists and recreates it
|
||||
func (n *node) recreateView() {
|
||||
var err error
|
||||
w := n.tk
|
||||
log.Log(ERROR, "recreateView() START", n.WidgetType, n.progname)
|
||||
if me.baseGui == nil {
|
||||
log.Log(ERROR, "recreateView() ERROR: me.baseGui == nil", w)
|
||||
return
|
||||
}
|
||||
|
||||
// this deletes the button from gocui
|
||||
me.baseGui.DeleteView(w.cuiName)
|
||||
w.v = nil
|
||||
|
||||
if n.progname == "CLOUDFLARE_EMAIL" {
|
||||
n.showWidgetPlacement(true, "n.progname="+n.progname+" n.tk.label="+n.tk.label+" "+w.cuiName)
|
||||
n.dumpWidget("jwc")
|
||||
n.textResize()
|
||||
n.showWidgetPlacement(true, "n.progname="+n.progname+" n.tk.label="+n.tk.label+" "+w.cuiName)
|
||||
}
|
||||
|
||||
a := w.gocuiSize.w0
|
||||
b := w.gocuiSize.h0
|
||||
c := w.gocuiSize.w1
|
||||
d := w.gocuiSize.h1
|
||||
|
||||
w.v, err = me.baseGui.SetView(w.cuiName, a, b, c, d, 0)
|
||||
if err == nil {
|
||||
n.showWidgetPlacement(true, "recreateView()")
|
||||
log.Log(ERROR, "recreateView() internal plugin error err = nil")
|
||||
return
|
||||
}
|
||||
if !errors.Is(err, gocui.ErrUnknownView) {
|
||||
n.showWidgetPlacement(true, "recreateView()")
|
||||
log.Log(ERROR, "recreateView() internal plugin error error.IS()", err)
|
||||
return
|
||||
}
|
||||
|
||||
// this sets up the keybinding for the name of the window
|
||||
// does this really need to be done? I think we probably already
|
||||
// know everything about where all the widgets are so we could bypass
|
||||
// the gocui package and just handle all the mouse events internally here (?)
|
||||
// for now, the w.v.Name is a string "1", "2", "3", etc from the widgetId
|
||||
|
||||
// set the binding for this gocui view now that it has been created
|
||||
// gocui handles overlaps of views so it will run on the view that is clicked on
|
||||
me.baseGui.SetKeybinding(w.v.Name(), gocui.MouseLeft, gocui.ModNone, click)
|
||||
|
||||
// this actually sends the text to display to gocui
|
||||
w.v.Wrap = true
|
||||
w.v.Frame = w.frame
|
||||
w.v.Clear()
|
||||
fmt.Fprint(w.v, n.tk.label)
|
||||
// n.showWidgetPlacement(true, "n.progname=" + n.progname + " n.tk.label=" + n.tk.label + " " + w.cuiName)
|
||||
// n.dumpWidget("jwc 2")
|
||||
|
||||
// if you don't do this here, it will be black & white only
|
||||
if w.color != nil {
|
||||
w.v.FrameColor = w.color.frame
|
||||
w.v.FgColor = w.color.fg
|
||||
w.v.BgColor = w.color.bg
|
||||
w.v.SelFgColor = w.color.selFg
|
||||
w.v.SelBgColor = w.color.selBg
|
||||
}
|
||||
if n.progname == "CLOUDFLARE_EMAIL" {
|
||||
n.showWidgetPlacement(true, "n.progname="+n.progname+" n.tk.label="+n.tk.label+" "+w.cuiName)
|
||||
n.dumpTree(true)
|
||||
}
|
||||
log.Log(ERROR, "recreateView() END")
|
||||
}
|
||||
|
||||
func (n *node) hideWidgets() {
|
||||
w := n.tk
|
||||
w.isCurrent = false
|
||||
switch n.WidgetType {
|
||||
switch w.WidgetType() {
|
||||
case widget.Root:
|
||||
case widget.Flag:
|
||||
case widget.Window:
|
||||
case widget.Box:
|
||||
case widget.Grid:
|
||||
default:
|
||||
n.hideView()
|
||||
w.Hide()
|
||||
}
|
||||
for _, child := range n.children {
|
||||
for _, child := range w.children {
|
||||
child.hideWidgets()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) hideFake() {
|
||||
w := n.tk
|
||||
if w.isFake {
|
||||
n.hideView()
|
||||
func hideFake() {
|
||||
var w *guiWidget
|
||||
w = me.treeRoot.TK.(*guiWidget)
|
||||
w.hideFake()
|
||||
}
|
||||
for _, child := range n.children {
|
||||
|
||||
func showFake() {
|
||||
var w *guiWidget
|
||||
w = me.treeRoot.TK.(*guiWidget)
|
||||
w.showFake()
|
||||
}
|
||||
|
||||
func (w *guiWidget) hideFake() {
|
||||
if w.isFake {
|
||||
w.Hide()
|
||||
}
|
||||
for _, child := range w.children {
|
||||
child.hideFake()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) showFake() {
|
||||
w := n.tk
|
||||
// shows the 'fake' widgets for widgets that
|
||||
// are not normally displayed (like a grid widget)
|
||||
func (w *guiWidget) showFake() {
|
||||
if w.isFake {
|
||||
n.setFake()
|
||||
n.showWidgetPlacement(true, "showFake:")
|
||||
n.showView()
|
||||
w.drawView()
|
||||
w.dumpWidget("in showFake()")
|
||||
}
|
||||
for _, child := range n.children {
|
||||
for _, child := range w.children {
|
||||
child.showFake()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) showWidgets() {
|
||||
w := n.tk
|
||||
if w.isFake {
|
||||
// don't display by default
|
||||
} else {
|
||||
n.showWidgetPlacement(true, "current:")
|
||||
n.showView()
|
||||
}
|
||||
for _, child := range n.children {
|
||||
func (w *guiWidget) showWidgets() {
|
||||
w.Show()
|
||||
|
||||
for _, child := range w.children {
|
||||
child.showWidgets()
|
||||
}
|
||||
}
|
||||
|
|
139
widget.go
139
widget.go
|
@ -1,139 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func initWidget(n *node) *guiWidget {
|
||||
var w *guiWidget
|
||||
w = new(guiWidget)
|
||||
// Set(w, "default")
|
||||
|
||||
w.frame = true
|
||||
|
||||
// set the name used by gocui to the id
|
||||
w.cuiName = string(n.WidgetId)
|
||||
|
||||
if n.WidgetType == widget.Root {
|
||||
log.Log(INFO, "setupWidget() FOUND ROOT w.id =", n.WidgetId)
|
||||
n.WidgetId = 0
|
||||
me.rootNode = n
|
||||
return w
|
||||
}
|
||||
|
||||
if n.WidgetType == widget.Grid {
|
||||
w.widths = make(map[int]int) // how tall each row in the grid is
|
||||
w.heights = make(map[int]int) // how wide each column in the grid is
|
||||
}
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func setupCtrlDownWidget() {
|
||||
a := new(widget.Action)
|
||||
a.ProgName = "ctrlDown"
|
||||
a.WidgetType = widget.Dialog
|
||||
a.WidgetId = -1
|
||||
a.ParentId = 0
|
||||
n := addNode(a)
|
||||
|
||||
me.ctrlDown = n
|
||||
}
|
||||
|
||||
func (n *node) deleteView() {
|
||||
w := n.tk
|
||||
if w.v != nil {
|
||||
w.v.Visible = false
|
||||
return
|
||||
}
|
||||
// make sure the view isn't really there
|
||||
me.baseGui.DeleteView(w.cuiName)
|
||||
w.v = nil
|
||||
}
|
||||
|
||||
// searches the binary tree for a WidgetId
|
||||
func (n *node) findWidgetName(name string) *node {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if n.tk.cuiName == name {
|
||||
return n
|
||||
}
|
||||
|
||||
for _, child := range n.children {
|
||||
newN := child.findWidgetName(name)
|
||||
if newN != nil {
|
||||
return newN
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) IsCurrent() bool {
|
||||
w := n.tk
|
||||
if n.WidgetType == widget.Tab {
|
||||
return w.isCurrent
|
||||
}
|
||||
if n.WidgetType == widget.Window {
|
||||
return w.isCurrent
|
||||
}
|
||||
if n.WidgetType == widget.Root {
|
||||
return false
|
||||
}
|
||||
return n.parent.IsCurrent()
|
||||
}
|
||||
|
||||
func (n *node) Visible() bool {
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
if n.tk == nil {
|
||||
return false
|
||||
}
|
||||
if n.tk.v == nil {
|
||||
return false
|
||||
}
|
||||
return n.tk.v.Visible
|
||||
}
|
||||
|
||||
func (n *node) SetVisible(b bool) {
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
if n.tk == nil {
|
||||
return
|
||||
}
|
||||
if n.tk.v == nil {
|
||||
return
|
||||
}
|
||||
n.tk.v.Visible = b
|
||||
}
|
||||
|
||||
func addDropdown() *node {
|
||||
n := new(node)
|
||||
n.WidgetType = widget.Flag
|
||||
n.WidgetId = -2
|
||||
n.ParentId = 0
|
||||
|
||||
// copy the data from the action message
|
||||
n.progname = "DropBox"
|
||||
n.tk.label = "DropBox text"
|
||||
|
||||
// store the internal toolkit information
|
||||
n.tk = new(guiWidget)
|
||||
n.tk.frame = true
|
||||
|
||||
// set the name used by gocui to the id
|
||||
n.tk.cuiName = "-1 dropbox"
|
||||
|
||||
n.tk.color = &colorFlag
|
||||
|
||||
// add this new widget on the binary tree
|
||||
n.parent = me.rootNode
|
||||
if n.parent != nil {
|
||||
n.parent.children = append(n.parent.children, n)
|
||||
}
|
||||
return n
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
log "go.wit.com/log"
|
||||
"go.wit.com/toolkits/tree"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
var fakeStartWidth int = me.FakeW
|
||||
var fakeStartHeight int = me.TabH + me.FramePadH
|
||||
|
||||
// setup fake labels for non-visible things off screen
|
||||
func setFake(n *tree.Node) {
|
||||
var w *guiWidget
|
||||
w = n.TK.(*guiWidget)
|
||||
w.isFake = true
|
||||
|
||||
w.gocuiSetWH(fakeStartWidth, fakeStartHeight)
|
||||
|
||||
fakeStartHeight += w.gocuiSize.Height()
|
||||
// TODO: use the actual max hight of the terminal window
|
||||
if fakeStartHeight > 24 {
|
||||
fakeStartHeight = me.TabH
|
||||
fakeStartWidth += me.FakeW
|
||||
}
|
||||
}
|
||||
|
||||
// mostly just sets the colors of things
|
||||
func addWidget(n *tree.Node) {
|
||||
if !me.ok {
|
||||
log.Log(INFO, "addWidget() START NOT OKAY")
|
||||
log.Log(INFO, "addWidget() START NOT OKAY")
|
||||
log.Log(INFO, "addWidget() START NOT OKAY")
|
||||
waitOK()
|
||||
}
|
||||
tk := n.TK.(*guiWidget)
|
||||
log.Log(INFO, "setStartWH() w.id =", n.WidgetId, "n.name", n.String())
|
||||
switch n.WidgetType {
|
||||
case widget.Root:
|
||||
log.Log(INFO, "setStartWH() rootNode w.id =", n.WidgetId, "w.name", n.String())
|
||||
setFake(n)
|
||||
return
|
||||
case widget.Flag:
|
||||
setFake(n)
|
||||
return
|
||||
case widget.Window:
|
||||
tk.frame = false
|
||||
tk.labelN = tk.GetText() + " X"
|
||||
me.newWindowTrigger <- tk
|
||||
redoWindows(0, 0)
|
||||
return
|
||||
case widget.Stdout:
|
||||
tk.labelN = "moreSTDOUT"
|
||||
n.State.ProgName = "moreSTDOUT"
|
||||
n.State.Label = "moreSTDOUT"
|
||||
tk.isFake = true
|
||||
return
|
||||
case widget.Tab:
|
||||
return
|
||||
case widget.Button:
|
||||
tk.setColorButton()
|
||||
if tk.IsEnabled() {
|
||||
} else {
|
||||
tk.setColorDisable()
|
||||
}
|
||||
return
|
||||
case widget.Checkbox:
|
||||
tk.setColorInput()
|
||||
tk.labelN = "X " + n.State.Label
|
||||
return
|
||||
case widget.Dropdown:
|
||||
tk.setColorInput()
|
||||
return
|
||||
case widget.Textbox:
|
||||
n.State.Label = ""
|
||||
tk.labelN = " "
|
||||
tk.setColorInput()
|
||||
return
|
||||
case widget.Combobox:
|
||||
tk.setColorInput()
|
||||
return
|
||||
case widget.Box:
|
||||
tk.isFake = true
|
||||
setFake(n)
|
||||
return
|
||||
case widget.Grid:
|
||||
tk.isFake = true
|
||||
setFake(n)
|
||||
return
|
||||
case widget.Group:
|
||||
tk.setColorLabel()
|
||||
tk.frame = false
|
||||
return
|
||||
case widget.Label:
|
||||
if tk.node.InTable() {
|
||||
if tk.node.State.AtH == 0 {
|
||||
// this is the table header
|
||||
tk.setColorLabelTable()
|
||||
} else {
|
||||
// todo: highlight the whole table row
|
||||
tk.setColorLabel()
|
||||
}
|
||||
} else {
|
||||
tk.setColorLabel()
|
||||
}
|
||||
tk.frame = false
|
||||
return
|
||||
default:
|
||||
/*
|
||||
if n.IsCurrent() {
|
||||
n.updateCurrent()
|
||||
}
|
||||
*/
|
||||
}
|
||||
tk.dumpWidget("addWidget()unknown")
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/toolkits/tree"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func initWidget(n *tree.Node) *guiWidget {
|
||||
var w *guiWidget
|
||||
w = new(guiWidget)
|
||||
|
||||
w.node = n
|
||||
w.cuiName = strconv.Itoa(w.WidgetId()) + " TK"
|
||||
// w.WidgetType() = n.WidgetType
|
||||
w.labelN = n.State.Label
|
||||
if w.labelN == "" {
|
||||
// remove this debugging hack once things are stable and fixed
|
||||
w.labelN = n.GetProgName()
|
||||
}
|
||||
w.frame = true
|
||||
w.enable = n.State.Enable
|
||||
|
||||
if n.WidgetType == widget.Root {
|
||||
log.Log(INFO, "setupWidget() FOUND ROOT w.id =", n.WidgetId)
|
||||
}
|
||||
|
||||
if n.WidgetType == widget.Grid {
|
||||
w.widths = make(map[int]int) // how tall each row in the grid is
|
||||
w.heights = make(map[int]int) // how wide each column in the grid is
|
||||
}
|
||||
|
||||
p := n.Parent
|
||||
if p == nil {
|
||||
log.Log(ERROR, "parent == nil", w.String(), n.WidgetId, w.WidgetType())
|
||||
return w
|
||||
}
|
||||
if p.TK == nil {
|
||||
if n.WidgetId == 0 {
|
||||
// this is a normal init condition
|
||||
} else {
|
||||
log.Log(ERROR, "parent.TK == nil", w.String(), n.WidgetId, w.WidgetType())
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// set the parent and append to parent children
|
||||
var ptk *guiWidget
|
||||
ptk = p.TK.(*guiWidget)
|
||||
w.parent = ptk
|
||||
ptk.children = append(ptk.children, w)
|
||||
return w
|
||||
}
|
||||
|
||||
func setupCtrlDownWidget() {
|
||||
a := new(widget.Action)
|
||||
a.ProgName = "ctrlDown"
|
||||
a.WidgetType = widget.Dialog
|
||||
a.WidgetId = -1
|
||||
a.ParentId = 0
|
||||
// n := addNode(a)
|
||||
n := me.myTree.AddNode(a)
|
||||
|
||||
me.ctrlDown = n
|
||||
}
|
||||
|
||||
func (w *guiWidget) deleteView() {
|
||||
// make sure the view isn't really there
|
||||
// log.Log(GOCUI, "deleteView()", w.cuiName, w.WidgetType(), w.WidgetId())
|
||||
me.baseGui.DeleteView(w.cuiName)
|
||||
w.v = nil
|
||||
}
|
||||
|
||||
func (tk *guiWidget) String() string {
|
||||
// deprecate this?
|
||||
curval := strings.TrimSpace(tk.labelN)
|
||||
if curval != "" {
|
||||
return curval
|
||||
}
|
||||
curval = strings.TrimSpace(tk.GetLabel())
|
||||
if curval != "" {
|
||||
return curval
|
||||
}
|
||||
curval = tk.GetText()
|
||||
if curval != "" {
|
||||
return curval
|
||||
}
|
||||
curval = tk.node.String()
|
||||
if curval != "" {
|
||||
return curval
|
||||
}
|
||||
curval = strings.TrimSpace(tk.node.ProgName())
|
||||
if curval != "" {
|
||||
return curval
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (tk *guiWidget) Visible() bool {
|
||||
if tk == nil {
|
||||
return false
|
||||
}
|
||||
if tk.v == nil {
|
||||
return false
|
||||
}
|
||||
tk.v.Visible = true
|
||||
return true
|
||||
}
|
||||
|
||||
func (tk *guiWidget) Hide() {
|
||||
tk.deleteView()
|
||||
}
|
||||
|
||||
func (tk *guiWidget) SetVisible(b bool) {
|
||||
if b {
|
||||
tk.Show()
|
||||
} else {
|
||||
tk.Hide()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
log "go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
// don't draw widgets that are too far down the window
|
||||
func (tk *guiWidget) doNotDraw() bool {
|
||||
var check bool
|
||||
switch tk.WidgetType() {
|
||||
case widget.Button:
|
||||
check = true
|
||||
case widget.Label:
|
||||
check = true
|
||||
default:
|
||||
}
|
||||
if !check {
|
||||
return false
|
||||
}
|
||||
win := tk.findParentWindow()
|
||||
if win == nil {
|
||||
// don't draw anything if you can't find the parent window
|
||||
return true
|
||||
}
|
||||
|
||||
h := tk.gocuiSize.h0 - win.gocuiSize.h0
|
||||
|
||||
if h > 20 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// page widgets in the window
|
||||
func (tk *guiWidget) pageWidget() *rectType {
|
||||
r := new(rectType)
|
||||
|
||||
var check bool
|
||||
switch tk.WidgetType() {
|
||||
case widget.Button:
|
||||
check = true
|
||||
case widget.Label:
|
||||
check = true
|
||||
default:
|
||||
}
|
||||
if !check {
|
||||
return nil
|
||||
}
|
||||
win := tk.findParentWindow()
|
||||
if win == nil {
|
||||
// don't draw anything if you can't find the parent window
|
||||
return nil
|
||||
}
|
||||
|
||||
r.w0 = tk.gocuiSize.w0
|
||||
r.h0 = tk.gocuiSize.h0
|
||||
r.w1 = tk.gocuiSize.w1
|
||||
r.h1 = tk.gocuiSize.h1
|
||||
|
||||
// r.h0 = tk.gocuiSize.h0 - win.gocuiSize.h0
|
||||
|
||||
if r.h0 > 20 {
|
||||
return r
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// display's the text of the widget in gocui
|
||||
// deletes the old view if it exists and recreates it
|
||||
func (tk *guiWidget) drawView() {
|
||||
var err error
|
||||
log.Log(INFO, "drawView() START", tk.WidgetType(), tk.String())
|
||||
if me.baseGui == nil {
|
||||
log.Log(ERROR, "drawView() ERROR: me.baseGui == nil", tk)
|
||||
return
|
||||
}
|
||||
|
||||
if tk.cuiName == "" {
|
||||
log.Log(ERROR, "drawView() tk.cuiName was not set for widget", tk)
|
||||
tk.cuiName = strconv.Itoa(tk.WidgetId()) + " TK"
|
||||
}
|
||||
log.Log(INFO, "drawView() labelN =", tk.labelN)
|
||||
|
||||
// this deletes the button from gocui
|
||||
me.baseGui.DeleteView(tk.cuiName)
|
||||
tk.v = nil
|
||||
|
||||
a := tk.gocuiSize.w0
|
||||
b := tk.gocuiSize.h0
|
||||
c := tk.gocuiSize.w1
|
||||
d := tk.gocuiSize.h1
|
||||
|
||||
/*
|
||||
// testing code for paging large windows
|
||||
if tk.doNotDraw() {
|
||||
return
|
||||
}
|
||||
|
||||
if tk.window.pager != 0 {
|
||||
if r := tk.pageWidget(); r == nil {
|
||||
// if nil, draw whatever it is anyway
|
||||
} else {
|
||||
if r.Width() == 0 && r.Height() == 0 {
|
||||
// don't draw empty stuff
|
||||
return
|
||||
}
|
||||
a = r.w0
|
||||
b = r.h0
|
||||
c = r.w1
|
||||
d = r.h1
|
||||
}
|
||||
}
|
||||
|
||||
if tk.WidgetType() == widget.Window || tk.WidgetType() == widget.Flag {
|
||||
if tk.window.pager != 0 {
|
||||
if tk.gocuiSize.Height() > 40 {
|
||||
tk.window.large = true
|
||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + 40
|
||||
d = tk.gocuiSize.h1
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// this is all terrible. This sets the title. kinda
|
||||
if tk.WidgetType() == widget.Window {
|
||||
tk.textResize()
|
||||
tk.full.w0 = tk.force.w0
|
||||
tk.full.h0 = tk.force.h0
|
||||
|
||||
// for windows, make it the full size
|
||||
a = tk.full.w0
|
||||
b = tk.full.h0
|
||||
c = tk.full.w0 + tk.gocuiSize.Width()
|
||||
d = tk.full.h0 + tk.gocuiSize.Height()
|
||||
} else {
|
||||
if tk.internal {
|
||||
// do nothing
|
||||
} else {
|
||||
tk.textResize() // resize gocui frame to the widget text
|
||||
}
|
||||
a = tk.gocuiSize.w0
|
||||
b = tk.gocuiSize.h0
|
||||
c = tk.gocuiSize.w1
|
||||
d = tk.gocuiSize.h1
|
||||
}
|
||||
|
||||
tk.v, err = me.baseGui.SetView(tk.cuiName, a, b, c, d, 0)
|
||||
if err == nil {
|
||||
tk.dumpWidget("drawView() err")
|
||||
log.Log(ERROR, "drawView() internal plugin error err = nil")
|
||||
return
|
||||
}
|
||||
|
||||
if !errors.Is(err, gocui.ErrUnknownView) {
|
||||
tk.dumpWidget("drawView() err")
|
||||
log.Log(ERROR, "drawView() internal plugin error error.IS()", err)
|
||||
return
|
||||
}
|
||||
|
||||
if tk.v == nil {
|
||||
log.Info("MUTEX FAIL. tk.v == nil here in drawView()")
|
||||
log.Info("MUTEX FAIL. tk.v == nil here in drawView()")
|
||||
log.Info("MUTEX FAIL. tk.v == nil here in drawView()")
|
||||
return
|
||||
}
|
||||
|
||||
// this actually sends the text to display to gocui
|
||||
tk.v.Wrap = true
|
||||
tk.v.Frame = tk.frame
|
||||
tk.v.Clear()
|
||||
fmt.Fprint(tk.v, tk.labelN)
|
||||
|
||||
// tmp hack to disable buttons on window open
|
||||
if tk.WidgetType() == widget.Button {
|
||||
if tk.IsEnabled() {
|
||||
} else {
|
||||
tk.setColorDisable()
|
||||
}
|
||||
}
|
||||
|
||||
switch tk.WidgetType() {
|
||||
case widget.Button:
|
||||
if tk.IsEnabled() {
|
||||
if tk.isDense() && tk.isInGrid() {
|
||||
tk.setColorButtonDense()
|
||||
} else {
|
||||
tk.setColorButton()
|
||||
}
|
||||
} else {
|
||||
tk.setColorDisable()
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
if tk.v == nil {
|
||||
log.Info("MUTEX FAIL 2. tk.v was deleted somehow tk.v == nil here in drawView()")
|
||||
log.Info("MUTEX FAIL 2. tk.v == nil here in drawView()")
|
||||
log.Info("MUTEX FAIL 2. tk.v == nil here in drawView()")
|
||||
return
|
||||
}
|
||||
|
||||
// if you don't do this here, it will be black & white only
|
||||
if tk.color != nil {
|
||||
tk.v.FrameColor = tk.color.frame
|
||||
tk.v.FgColor = tk.color.fg
|
||||
tk.v.BgColor = tk.color.bg
|
||||
tk.v.SelFgColor = tk.color.selFg
|
||||
tk.v.SelBgColor = tk.color.selBg
|
||||
}
|
||||
|
||||
log.Log(INFO, "drawView() END")
|
||||
}
|
||||
|
||||
// redraw the widget tree starting at this location
|
||||
func (w *guiWidget) DrawAt(offsetW, offsetH int) {
|
||||
w.placeWidgets(offsetW, offsetH) // compute the sizes & places for each widget
|
||||
// w.dumpWidget(fmt.Sprintf("DrawAt(%d,%d)", offsetW, offsetH))
|
||||
}
|
||||
|
||||
// display the widgets in the binary tree
|
||||
func (w *guiWidget) drawTree(draw bool) {
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
w.dumpWidget("in drawTree()")
|
||||
if draw {
|
||||
// w.textResize()
|
||||
w.Show()
|
||||
} else {
|
||||
w.Hide()
|
||||
}
|
||||
|
||||
for _, child := range w.children {
|
||||
child.drawTree(draw)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *guiWidget) Show() {
|
||||
if w.isFake {
|
||||
// don't display fake widgets
|
||||
return
|
||||
}
|
||||
|
||||
if w.Hidden() {
|
||||
// recursively checks if the parent is hidden
|
||||
// never show hidden widgets
|
||||
return
|
||||
}
|
||||
if me.debug {
|
||||
w.dumpWidget("drawView()")
|
||||
}
|
||||
|
||||
w.drawView()
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
log "go.wit.com/log"
|
||||
"go.wit.com/toolkits/tree"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func (tk *guiWidget) setTitle(s string) {
|
||||
if tk.WidgetType() != widget.Window {
|
||||
return
|
||||
}
|
||||
if tk.v == nil {
|
||||
return
|
||||
}
|
||||
tk.setColorWindowTitleActive()
|
||||
rect := tk.gocuiSize
|
||||
rect.w1 = rect.w0 + tk.full.Width() + 1
|
||||
// rect.h1 = rect.h0 + 1
|
||||
me.baseGui.SetView(tk.v.Name(), rect.w0-1, rect.h0, rect.w1+1, rect.h1, 0)
|
||||
tk.v.Clear()
|
||||
f := " %-" + fmt.Sprintf("%d", tk.full.Width()-3) + "s %s"
|
||||
tmp := tk.GetLabel()
|
||||
labelN := fmt.Sprintf(f, tmp, "X")
|
||||
tk.v.WriteString(labelN)
|
||||
}
|
||||
|
||||
func (tk *guiWidget) redrawWindow(w int, h int) {
|
||||
if tk.WidgetType() != widget.Window {
|
||||
return
|
||||
}
|
||||
// tk.dumpWidget(fmt.Sprintf("redrawWindow(%d,%d)", w, h))
|
||||
if tk.full.Height() > 40 {
|
||||
tk.window.dense = true
|
||||
}
|
||||
// pin the window to (w,h)
|
||||
tk.gocuiSize.w0 = w
|
||||
tk.gocuiSize.h0 = h
|
||||
tk.gocuiSize.w1 = w + len(tk.GetLabel())
|
||||
tk.labelN = tk.GetLabel() // could set XX here also but don't have final size of window yet
|
||||
tk.force.w0 = w
|
||||
tk.force.w1 = w
|
||||
tk.force.h0 = h
|
||||
tk.force.h1 = h
|
||||
|
||||
tk.setFullSize() // might make the green box the right size
|
||||
|
||||
tk.frame = false
|
||||
tk.hasTabs = false
|
||||
|
||||
tk.DrawAt(w, h)
|
||||
// tk.setColor(&colorActiveW) // sets the window to Green BG
|
||||
tk.setColorWindowTitleActive()
|
||||
|
||||
if tk.window.collapsed {
|
||||
// don't show anything but the title bar
|
||||
tk.hideWindow()
|
||||
return
|
||||
}
|
||||
tk.placeWidgets(w, h) // compute the sizes & places for each widget
|
||||
|
||||
// this is a test. this should not be needed
|
||||
tk.full.w0 = tk.force.w0
|
||||
tk.full.h0 = tk.force.h0
|
||||
|
||||
tk.setFullSize()
|
||||
tk.Show()
|
||||
if tk.v == nil {
|
||||
log.Info("redrawWindow on tk.v == nil")
|
||||
standardExit()
|
||||
}
|
||||
tk.v.Clear()
|
||||
fmt.Fprint(tk.v, "ZZZ"+tk.GetText())
|
||||
tk.showWidgets()
|
||||
|
||||
if tk.windowFrame == nil {
|
||||
tk.addWindowFrameTK(0 - tk.WidgetId())
|
||||
tk.windowFrame.makeTK([]string{""})
|
||||
}
|
||||
|
||||
// this seems to correctly create the window frame
|
||||
r := tk.getFullSize()
|
||||
tk.windowFrame.gocuiSize.w0 = tk.force.w0
|
||||
tk.windowFrame.gocuiSize.w1 = r.w1 + 1
|
||||
tk.windowFrame.gocuiSize.h0 = tk.force.h0 + 2
|
||||
tk.windowFrame.gocuiSize.h1 = r.h1 + 1
|
||||
tk.windowFrame.full.w0 = tk.force.w0
|
||||
tk.windowFrame.full.w1 = r.w1 + 1
|
||||
tk.windowFrame.full.h0 = tk.force.h0 + 2
|
||||
tk.windowFrame.full.h1 = r.h1 + 1
|
||||
tk.windowFrame.setColorWindowFrame()
|
||||
tk.windowFrame.Hide()
|
||||
tk.windowFrame.Show()
|
||||
|
||||
// set the window frame below the window widget, but this resizes the window widget it seems
|
||||
me.baseGui.SetViewBeneath(tk.windowFrame.cuiName, tk.cuiName, 1)
|
||||
|
||||
// so now we have to resize the window frame, but this moves it to the top?
|
||||
me.baseGui.SetView(tk.windowFrame.cuiName, tk.windowFrame.full.w0, tk.windowFrame.full.h0, tk.windowFrame.full.w1, tk.windowFrame.full.h1, 0)
|
||||
|
||||
// so we have to redraw the widgets in the window anyway and then they will appear above he frame
|
||||
tk.hideWidgets()
|
||||
tk.showWidgets()
|
||||
|
||||
// draw the window title
|
||||
tk.setTitle(tk.GetLabel())
|
||||
}
|
||||
|
||||
// re-draws the buttons for each of the windows
|
||||
func redoWindows(nextW int, nextH int) {
|
||||
for _, tk := range findWindows() {
|
||||
// tk.dumpWidget(fmt.Sprintf("redoWindowsS (%d,%d)", nextW, nextH))
|
||||
if tk.window.wasDragged {
|
||||
// don't move windows around the user has dragged to a certain location
|
||||
tk.makeWindowActive()
|
||||
} else {
|
||||
w, _ := me.baseGui.Size()
|
||||
if nextW > w-20 {
|
||||
nextW = 0
|
||||
nextH += 4
|
||||
}
|
||||
// probably a new window?
|
||||
tk.redrawWindow(nextW, nextH)
|
||||
}
|
||||
// tk.dumpWidget(fmt.Sprintf("redoWindowsE (%d,%d)", nextW, nextH))
|
||||
|
||||
// increment the width for the next window
|
||||
nextW += tk.gocuiSize.Width() + 10
|
||||
// nextH += 10
|
||||
}
|
||||
}
|
||||
|
||||
func (tk *guiWidget) addWindowFrameTK(wId int) {
|
||||
n := tk.addWindowFrame(wId)
|
||||
tk.windowFrame = n.TK.(*guiWidget)
|
||||
}
|
||||
|
||||
func (win *guiWidget) addWindowFrame(wId int) *tree.Node {
|
||||
n := new(tree.Node)
|
||||
n.WidgetType = widget.Flag
|
||||
n.WidgetId = wId
|
||||
n.ParentId = 0
|
||||
|
||||
// store the internal toolkit information
|
||||
tk := new(guiWidget)
|
||||
tk.frame = true
|
||||
tk.labelN = "windowFrame text"
|
||||
tk.internal = true
|
||||
|
||||
tk.node = n
|
||||
if tk.node.Parent == nil {
|
||||
tk.node.Parent = me.treeRoot
|
||||
}
|
||||
|
||||
// set the name used by gocui to the id
|
||||
tk.cuiName = fmt.Sprintf("%d DR", wId)
|
||||
|
||||
// tk.color = &colorGroup
|
||||
|
||||
// add this new widget on the binary tree
|
||||
tk.parent = win
|
||||
if tk.parent == nil {
|
||||
panic("addDropdown() didn't get treeRoot guiWidget")
|
||||
} else {
|
||||
tk.parent.children = append(tk.parent.children, tk)
|
||||
}
|
||||
|
||||
n.TK = tk
|
||||
return n
|
||||
}
|
||||
|
||||
func (tk *guiWidget) isWindowActive() bool {
|
||||
if !(tk.WidgetType() == widget.Window || tk.WidgetType() == widget.Stdout) {
|
||||
// only allow Window or the Stdout widgets to be made active
|
||||
return false
|
||||
}
|
||||
return tk.window.active
|
||||
}
|
||||
|
||||
// always redraws at the corner of the gocuiSize box
|
||||
func (tk *guiWidget) makeWindowActive() {
|
||||
if !(tk.WidgetType() == widget.Window || tk.WidgetType() == widget.Stdout) {
|
||||
// only allow Window or the Stdout widgets to be made active
|
||||
return
|
||||
}
|
||||
|
||||
if tk.WidgetType() == widget.Stdout {
|
||||
me.stdout.outputOnTop = true
|
||||
} else {
|
||||
// me.stdout.outputOnTop = false // ?
|
||||
}
|
||||
|
||||
// disable and increment all the windows
|
||||
for _, tk := range me.allwin {
|
||||
tk.window.order += 1
|
||||
tk.window.active = false
|
||||
// tk.setColor(&colorWindow) // color for inactive windows
|
||||
tk.setColorWindowTitle()
|
||||
}
|
||||
|
||||
// set this window as the active one
|
||||
tk.window.active = true
|
||||
tk.window.order = 0
|
||||
|
||||
tk.redrawWindow(tk.gocuiSize.w0, tk.gocuiSize.h0)
|
||||
setThingsOnTop() // sets help, Stdout, etc on the top after windows have been redrawn
|
||||
}
|
||||
|
||||
func (tk *guiWidget) makeTK(ddItems []string) {
|
||||
items := strings.Join(ddItems, "\n")
|
||||
tk.labelN = items
|
||||
tk.SetText(items)
|
||||
tk.gocuiSize.w0 = 100
|
||||
tk.gocuiSize.w1 = 120
|
||||
tk.gocuiSize.h0 = 15
|
||||
tk.gocuiSize.h1 = 18
|
||||
|
||||
tk.Show()
|
||||
}
|
||||
|
||||
func (win *guiWidget) checkWindowClose(w int, h int) bool {
|
||||
s := fmt.Sprintf("mouse(%d,%d) ", w, h)
|
||||
offW := win.full.w1 - w
|
||||
offH := h - win.full.h0
|
||||
s += fmt.Sprintf("offset(%d,%d)", offW, offH)
|
||||
if (offW < 2) && (offH < 2) {
|
||||
log.Info("attempting close on ", s, win.cuiName)
|
||||
me.myTree.SendWindowCloseEvent(win.node)
|
||||
// store the stdout corner for computing the drag size
|
||||
return true
|
||||
}
|
||||
// log.Info("not attempting close on ", s, win.cuiName)
|
||||
return false
|
||||
}
|
Loading…
Reference in New Issue