I've created a box-select feature for tk.Text
. The widget gets the font
height and concocts an .xbm
image from it. The .xbm
is used as a faux-caret, via image_create
, for all selected lines except the line the real caret is on.
How do I make the faux-caret image instance(s) blink in time with the real caret?
or
What is another direction I can go to get these results?
CodePudding user response:
My solution was to get rid of the real caret visibly, draw a faux caret on every line, and then call a function that keeps swapping out an 'on' faux-caret with an 'off' faux-caret. Below are some relevant code snippets. Note: I actually have 3 faux-carets because I want the caret that is on the true active line to be a little darker.
#FAUX-CARET
def __loadcarets(self) -> None:
#store insert on/off times for faux-caret `.after` calls
self.__instime = (self['insertofftime'], self['insertontime'])
fh = self.__fh #font height from 'linespace'
#make a temp xbm file
with tempfile.NamedTemporaryFile(mode='w b', suffix='.xbm', delete=False) as f:
#create prettyprint xbm data
xbmdata = ',\n\t'.join(','.join('0xFF' for _ in range(min(8, fh-(8*i)))) for i in range(math.ceil(fh/8)))
#write xbm
f.write((f"#define image_width {INSWIDTH}\n#define image_height {fh}\n"
"static unsigned char image_bits[] = {\n\t"
f'{xbmdata}}};').encode())
#load xbm files for faux-caret ~ they have to be in this order
self.__fc = (tk.BitmapImage(file=f.name, foreground='#999999'), #shadow caret
tk.BitmapImage(file=f.name, foreground=self['background']), #off caret
tk.BitmapImage(file=f.name, foreground=self['insertbackground'])) #main caret
#delete file
os.unlink(f.name)
#faux-caret create or config
def __fauxcaret(self, index:str, on:bool=True, main:bool=False, cfg:bool=False) -> None:
(self.image_create, self.image_configure)[cfg](index, image=self.__fc[(main<<on)|(on^1)])
#blink the faux-caret(s)
def __blink(self, on:bool=True):
#nothing to do
if not self.__boxselect: return
#sort image indexes
if idx:=sorted((self.index(n) for n in self.image_names()), key=lambda i: float(i)):
#flip `on`
on = not on
#consider direction in forward perspective
fw = not self.__lbounds.rv
#reconfigure all carets
for i in idx: self.__fauxcaret(i, on=on, cfg=True)
#reconfigure "active line" caret, if off it will assign off again
self.__fauxcaret(idx[-fw], on=on, main=True, cfg=True)
#schedule next call
self.after(self.__instime[on], self.__blink, on)