Skip to content

API Reference

Editor

Bases: Frame

Editor class Picks the right editor based on the path, path2, diff values passed. Supports showing diff, images, text files. If nothing is passed, empty text editor is opened.

Parameters:

Name Type Description Default
path str

path of the file to be opened

None
path2 str

path of file to be opened in diff, required if diff=True is passed

None
diff bool

whether this is to be opened in diff editor

False
language str

Use the Languages enum provided (eg. Languages.PYTHON, Languages.TYPESCRIPT) This is given priority while picking suitable highlighter. If not passed, guesses from file extension.

None
dark_mode

Sets the editor theme to cupcake dark if True, or cupcake light by default This is ignored if custom config_file path is passed

required
config_file str

path to the custom config (TOML) file, uses theme defaults if not passed

None
showpath bool

whether to show the breadcrumbs for editor or not

True
font str | Font

Font used in line numbers, text editor, autocomplete. defaults to Consolas(11)

None
uifont str | Font

Font used for other UI components (breadcrumbs, trees)

None
preview_file_callback Callable

called when files in breadcrumbs-pathview are single clicked. MUST take an argument (path)

None
open_file_callback Callable

called when files in breadcrumbs-pathview are double clicked. MUST take an argument (path)

None

NOTE: All the standard tk.Text methods and properties are available under Editor.content (eg. Editor.content.insert, Editor.content.get)

Methods:

Name Description
save

If the content is editable writes to the specified path.

focus

Gives focus to the content.

Source code in cupcake\__init__.py
class Editor(Frame):
    """
    Editor class
    Picks the right editor based on the path, path2, diff values passed. Supports showing diff, images, text files.
    If nothing is passed, empty text editor is opened.

    Args:
        path: path of the file to be opened
        path2: path of file to be opened in diff, required if diff=True is passed
        diff: whether this is to be opened in diff editor
        language: Use the `Languages` enum provided (eg. Languages.PYTHON, Languages.TYPESCRIPT)
            This is given priority while picking suitable highlighter. If not passed, guesses from file extension.
        dark_mode: Sets the editor theme to cupcake dark if True, or cupcake light by default
            This is ignored if custom config_file path is passed
        config_file: path to the custom config (TOML) file, uses theme defaults if not passed
        showpath:  whether to show the breadcrumbs for editor or not
        font:  Font used in line numbers, text editor, autocomplete. defaults to Consolas(11)
        uifont: Font used for other UI components (breadcrumbs, trees)
        preview_file_callback: called when files in breadcrumbs-pathview are single clicked. MUST take an argument (path)
        open_file_callback:  called when files in breadcrumbs-pathview are double clicked. MUST take an argument (path)


    NOTE: All the standard *tk.Text* methods and properties are available under *Editor.content* (eg. Editor.content.insert, Editor.content.get)

    Methods:
        save: If the content is editable writes to the specified path.
        focus: Gives focus to the content.
    """

    def __init__(
        self,
        master,
        path: str = None,
        path2: str = None,
        diff: bool = False,
        language: str = None,
        darkmode=True,
        config_file: str = None,
        showpath: bool = True,
        font: str | Font = None,
        uifont: str | Font = None,
        preview_file_callback: typing.Callable = None,
        open_file_callback: typing.Callable = None,
        *args,
        **kwargs
    ) -> None:
        super().__init__(master, *args, **kwargs)

        self.path = path
        self.path2 = path2
        self.diff = diff
        self.showpath = showpath
        self.darkmode = darkmode
        self.config_file = config_file
        self.preview_file_callback = preview_file_callback
        self.open_file_callback = open_file_callback

        self.settings = Config(self, config_file, darkmode, font, uifont)
        self.theme = self.settings.theme

        self.config(bg=self.theme.border)
        self.grid_columnconfigure(0, weight=1)

        self.content = get_editor(self, path, path2, diff, language)
        self.filename = os.path.basename(self.path) if path else None
        if path and self.showpath and not diff:
            self.breadcrumbs = BreadCrumbs(self, path)
            self.grid_rowconfigure(1, weight=1)
            self.breadcrumbs.grid(row=0, column=0, sticky=tk.EW, pady=(0, 1))
            self.content.grid(row=1, column=0, sticky=tk.NSEW)
        else:
            self.grid_rowconfigure(0, weight=1)
            self.content.grid(row=0, column=0, sticky=tk.NSEW)

    def save(self, path: str = None) -> None:
        """Saves the content to the specified path

        Args:
            path (str, optional): path to save the content. Defaults to None.
        """
        self.content.save(path)

    def focus(self) -> None:
        """Gives focus to the content"""

        self.content.focus()

focus()

Gives focus to the content

Source code in cupcake\__init__.py
def focus(self) -> None:
    """Gives focus to the content"""

    self.content.focus()

save(path=None)

Saves the content to the specified path

Parameters:

Name Type Description Default
path str

path to save the content. Defaults to None.

None
Source code in cupcake\__init__.py
def save(self, path: str = None) -> None:
    """Saves the content to the specified path

    Args:
        path (str, optional): path to save the content. Defaults to None.
    """
    self.content.save(path)

Diff Editor

Bases: BaseEditor

DiffEditor class.

Parameters:

Name Type Description Default
master

Parent widget.

required
path1 str

Path to the first file.

required
path2 str

Path to the second file.

required
language

Language to use for syntax highlighting.

None
Source code in cupcake\diffeditor\__init__.py
class DiffEditor(BaseEditor):
    """DiffEditor class.

    Args:
        master: Parent widget.
        path1: Path to the first file.
        path2: Path to the second file.
        language: Language to use for syntax highlighting.
    """

    def __init__(self, master, path1: str, path2: str, language=None, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.config(bg=self.base.theme.border)
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=1)
        self.grid_rowconfigure(0, weight=1)
        self.path1 = path1
        self.path2 = path2
        self.editable = True

        self.lhs_data = []
        self.rhs_data = []

        self.lhs_last_line = 0
        self.rhs_last_line = 0

        self.lhs = DiffPane(self, path1, language=language)
        self.lhs.grid(row=0, column=0, sticky=tk.NSEW, padx=(0, 1))

        self.rhs = DiffPane(self, path2, language=language)
        self.rhs.grid(row=0, column=1, sticky=tk.NSEW)

        self.left = self.lhs.text
        self.right = self.text = self.rhs.text

        self.lhs.scrollbar["command"] = self.on_scrollbar
        self.rhs.scrollbar["command"] = self.on_scrollbar
        self.left["yscrollcommand"] = self.on_textscroll
        self.right["yscrollcommand"] = self.on_textscroll

        self.stipple = self.base.settings.stipple

        self.left.tag_config(
            "addition",
            background=self.base.theme.diffeditor.notexist,
            bgstipple=f"@{self.stipple}",
        )
        self.left.tag_config("removal", background=self.base.theme.diffeditor.removed)
        self.left.tag_config(
            "removedword", background=self.base.theme.diffeditor.removedword
        )

        self.right.tag_config("addition", background=self.base.theme.diffeditor.added)
        self.right.tag_config(
            "removal",
            background=self.base.theme.diffeditor.notexist,
            bgstipple=f"@{self.stipple}",
        )
        self.right.tag_config(
            "addedword", background=self.base.theme.diffeditor.addedword
        )

        self.differ = Differ(self)

        if path1 and path2:
            with open(self.path1, "r") as f:
                self.lhs_data = f.read()
            with open(self.path2, "r") as f:
                self.rhs_data = f.read()

    def on_scrollbar(self, *args):
        self.left.yview(*args)
        self.lhs.on_scroll()

        self.right.yview(*args)
        self.rhs.on_scroll()

    def on_textscroll(self, *args):
        self.lhs.scrollbar.set(*args)
        self.rhs.scrollbar.set(*args)
        self.on_scrollbar("moveto", args[0])

    def run_show_diff(self):
        """Run the show_diff method in a separate thread."""

        threading.Thread(target=self.show_diff).start()

    def show_diff_text(self, lhs, rhs):
        """Show the diff between the two texts.

        Args:
            lhs (str): The left-hand side text.
            rhs (str): The right-hand side text.
        """

        self.lhs_data = lhs
        self.rhs_data = rhs

        self.show_diff()

    def show_diff(self):
        """Show the diff between the two files."""

        self.left.set_active(True)
        self.lhs.clear()
        self.rhs.clear()

        lhs_lines = [line + "\n" for line in self.lhs_data.split("\n")]
        rhs_lines = [line + "\n" for line in self.rhs_data.split("\n")]

        self.diff = list(self.differ.get_diff(lhs_lines, rhs_lines))
        for i, line in enumerate(self.diff):
            marker = line[0]
            content = line[2:]

            match marker:
                case " ":
                    # line is same in both
                    self.left.write(content)
                    self.right.write(content)

                case "-":
                    # line is only on the left
                    self.left.write(content, "removal")
                    self.left.newline("addition")

                case "+":
                    # line is only on the right
                    self.right.write(content, "addition")
                    self.left.newline("addition")

                # case "?":
                #     # the above line has changes
                #     if matches := re.finditer(r'\++', content):
                #         self.left.delete(str(float(self.rhs_last_line+1)), str(float(int(float(self.left.index(tk.INSERT))))))
                #         for match in matches:
                #             start = f"{self.rhs_last_line}.{match.start()}"
                #             end = f"{self.rhs_last_line}.{match.end()}"
                #             self.right.tag_add("addedword", start, end)

                #     if matches := re.finditer(r'-+', content):
                #         self.right.delete(str(float(self.lhs_last_line+1)), str(float(int(float(self.right.index(tk.INSERT))))))
                #         for match in matches:
                #             start = f"{self.lhs_last_line}.{match.start()}"
                #             end = f"{self.lhs_last_line}.{match.end()}"
                #             self.left.tag_add("removedword", start, end)

            self.left.update()
            self.right.update()

        self.left.highlighter.highlight()
        self.right.highlighter.highlight()

        # Add extra empty lines at the bottom if one side has fewer lines
        lhs_line_count = int(float(self.left.index(tk.END))) - 1
        rhs_line_count = int(float(self.right.index(tk.END))) - 1
        if lhs_line_count > rhs_line_count:
            extra_newlines = lhs_line_count - rhs_line_count
            for _ in range(extra_newlines):
                self.right.newline()
        elif rhs_line_count > lhs_line_count:
            extra_newlines = rhs_line_count - lhs_line_count
            for _ in range(extra_newlines):
                self.left.newline()

        self.left.set_active(False)

run_show_diff()

Run the show_diff method in a separate thread.

Source code in cupcake\diffeditor\__init__.py
def run_show_diff(self):
    """Run the show_diff method in a separate thread."""

    threading.Thread(target=self.show_diff).start()

show_diff()

Show the diff between the two files.

Source code in cupcake\diffeditor\__init__.py
def show_diff(self):
    """Show the diff between the two files."""

    self.left.set_active(True)
    self.lhs.clear()
    self.rhs.clear()

    lhs_lines = [line + "\n" for line in self.lhs_data.split("\n")]
    rhs_lines = [line + "\n" for line in self.rhs_data.split("\n")]

    self.diff = list(self.differ.get_diff(lhs_lines, rhs_lines))
    for i, line in enumerate(self.diff):
        marker = line[0]
        content = line[2:]

        match marker:
            case " ":
                # line is same in both
                self.left.write(content)
                self.right.write(content)

            case "-":
                # line is only on the left
                self.left.write(content, "removal")
                self.left.newline("addition")

            case "+":
                # line is only on the right
                self.right.write(content, "addition")
                self.left.newline("addition")

            # case "?":
            #     # the above line has changes
            #     if matches := re.finditer(r'\++', content):
            #         self.left.delete(str(float(self.rhs_last_line+1)), str(float(int(float(self.left.index(tk.INSERT))))))
            #         for match in matches:
            #             start = f"{self.rhs_last_line}.{match.start()}"
            #             end = f"{self.rhs_last_line}.{match.end()}"
            #             self.right.tag_add("addedword", start, end)

            #     if matches := re.finditer(r'-+', content):
            #         self.right.delete(str(float(self.lhs_last_line+1)), str(float(int(float(self.right.index(tk.INSERT))))))
            #         for match in matches:
            #             start = f"{self.lhs_last_line}.{match.start()}"
            #             end = f"{self.lhs_last_line}.{match.end()}"
            #             self.left.tag_add("removedword", start, end)

        self.left.update()
        self.right.update()

    self.left.highlighter.highlight()
    self.right.highlighter.highlight()

    # Add extra empty lines at the bottom if one side has fewer lines
    lhs_line_count = int(float(self.left.index(tk.END))) - 1
    rhs_line_count = int(float(self.right.index(tk.END))) - 1
    if lhs_line_count > rhs_line_count:
        extra_newlines = lhs_line_count - rhs_line_count
        for _ in range(extra_newlines):
            self.right.newline()
    elif rhs_line_count > lhs_line_count:
        extra_newlines = rhs_line_count - lhs_line_count
        for _ in range(extra_newlines):
            self.left.newline()

    self.left.set_active(False)

show_diff_text(lhs, rhs)

Show the diff between the two texts.

Parameters:

Name Type Description Default
lhs str

The left-hand side text.

required
rhs str

The right-hand side text.

required
Source code in cupcake\diffeditor\__init__.py
def show_diff_text(self, lhs, rhs):
    """Show the diff between the two texts.

    Args:
        lhs (str): The left-hand side text.
        rhs (str): The right-hand side text.
    """

    self.lhs_data = lhs
    self.rhs_data = rhs

    self.show_diff()

Config

Config class.

Parameters:

Name Type Description Default
master

Parent widget.

required
config_file

Path to the config file.

''
darkmode

Whether to use dark mode.

True
font

Font to use for the text.

''
uifont

Font to use for the UI.

''

Attributes:

Name Type Description
base

Parent widget.

dir

Directory of the module.

stipple

Path to the stipple file.

font

Font to use for the text.

uifont

Font to use for the UI.

theme

SimpleNamespace object containing the theme settings.

syntax

Syntax settings.

style

Style

Source code in cupcake\config\__init__.py
class Config:
    """Config class.

    Args:
        master: Parent widget.
        config_file: Path to the config file.
        darkmode: Whether to use dark mode.
        font: Font to use for the text.
        uifont: Font to use for the UI.

    Attributes:
        base: Parent widget.
        dir: Directory of the module.
        stipple: Path to the stipple file.
        font: Font to use for the text.
        uifont: Font to use for the UI.
        theme: SimpleNamespace object containing the theme settings.
        syntax: Syntax settings.
        style: Style
    """

    def __init__(self, master, config_file="", darkmode=True, font="", uifont=""):
        self.base = master
        self.dir = os.path.dirname(__file__)

        self.stipple = os.path.join(self.dir, "stipple.xbm")
        if not config_file:
            config_file = (
                os.path.join(self.dir, "dark.toml")
                if darkmode
                else os.path.join(self.dir, "light.toml")
            )

        self.font = font or Font(family="Consolas", size=11)
        self.uifont = uifont or Font(family="Segoi UI", size=10)
        self.load_from(config_file)

    def load_from(self, config_file: str):
        self.theme = SimpleNamespace(**toml.load(config_file))
        self.theme.editor = SimpleNamespace(**self.theme.editor)
        self.theme.diffeditor = SimpleNamespace(**self.theme.diffeditor)
        self.syntax = self.theme.syntax

        self.style = Style(self.base, self)

Languages Enum

Holds all available languages. To be passed to Editor during initialization.

Source code in cupcake\languages.py
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
class Languages:
    """
    Holds all available languages. To be passed to Editor during initialization.
    """

    ABAP = "abap"
    AMDGPU = "amdgpu"
    APL = "apl"
    ABNF = "abnf"
    ACTIONSCRIPT3 = "actionscript3"
    ACTIONSCRIPT = "actionscript"
    ADA = "ada"
    ADL = "adl"
    AGDA = "agda"
    AHEUI = "aheui"
    ALLOY = "alloy"
    AMBIENTTALK = "ambienttalk"
    AMPL = "ampl"
    HTML_NG2 = "html+ng2"
    NG2 = "ng2"
    ANTLR_ACTIONSCRIPT = "antlr-actionscript"
    ANTLR_CSHARP = "antlr-csharp"
    ANTLR_CPP = "antlr-cpp"
    ANTLR_JAVA = "antlr-java"
    ANTLR = "antlr"
    ANTLR_OBJC = "antlr-objc"
    ANTLR_PERL = "antlr-perl"
    ANTLR_PYTHON = "antlr-python"
    ANTLR_RUBY = "antlr-ruby"
    APACHECONF = "apacheconf"
    APPLESCRIPT = "applescript"
    ARDUINO = "arduino"
    ARROW = "arrow"
    ARTURO = "arturo"
    ASC = "asc"
    ASPECTJ = "aspectj"
    ASYMPTOTE = "asymptote"
    AUGEAS = "augeas"
    AUTOIT = "autoit"
    AUTOHOTKEY = "autohotkey"
    AWK = "awk"
    BBCBASIC = "bbcbasic"
    BBCODE = "bbcode"
    BC = "bc"
    BST = "bst"
    BARE = "bare"
    BASEMAKE = "basemake"
    BASH = "bash"
    CONSOLE = "console"
    BATCH = "batch"
    BDD = "bdd"
    BEFUNGE = "befunge"
    BERRY = "berry"
    BIBTEX = "bibtex"
    BLITZBASIC = "blitzbasic"
    BLITZMAX = "blitzmax"
    BNF = "bnf"
    BOA = "boa"
    BOO = "boo"
    BOOGIE = "boogie"
    BRAINFUCK = "brainfuck"
    BUGS = "bugs"
    CAMKES = "camkes"
    C = "c"
    CMAKE = "cmake"
    C_OBJDUMP = "c-objdump"
    CPSA = "cpsa"
    CSS_UL4 = "css+ul4"
    ASPX_CS = "aspx-cs"
    CSHARP = "csharp"
    CA65 = "ca65"
    CADL = "cadl"
    CAPDL = "capdl"
    CAPNP = "capnp"
    CBMBAS = "cbmbas"
    CDDL = "cddl"
    CEYLON = "ceylon"
    CFENGINE3 = "cfengine3"
    CHAISCRIPT = "chaiscript"
    CHAPEL = "chapel"
    CHARMCI = "charmci"
    HTML_CHEETAH = "html+cheetah"
    JAVASCRIPT_CHEETAH = "javascript+cheetah"
    CHEETAH = "cheetah"
    XML_CHEETAH = "xml+cheetah"
    CIRRU = "cirru"
    CLAY = "clay"
    CLEAN = "clean"
    CLOJURE = "clojure"
    CLOJURESCRIPT = "clojurescript"
    COBOLFREE = "cobolfree"
    COBOL = "cobol"
    COFFEESCRIPT = "coffeescript"
    CFC = "cfc"
    CFM = "cfm"
    CFS = "cfs"
    COMAL = "comal"
    COMMON_LISP = "common-lisp"
    COMPONENTPASCAL = "componentpascal"
    COQ = "coq"
    CPLINT = "cplint"
    CPP = "cpp"
    CPP_OBJDUMP = "cpp-objdump"
    CRMSH = "crmsh"
    CROC = "croc"
    CRYPTOL = "cryptol"
    CR = "cr"
    CSOUND_DOCUMENT = "csound-document"
    CSOUND = "csound"
    CSOUND_SCORE = "csound-score"
    CSS_DJANGO = "css+django"
    CSS_RUBY = "css+ruby"
    CSS_GENSHITEXT = "css+genshitext"
    CSS = "css"
    CSS_PHP = "css+php"
    CSS_SMARTY = "css+smarty"
    CUDA = "cuda"
    CYPHER = "cypher"
    CYTHON = "cython"
    D = "d"
    D_OBJDUMP = "d-objdump"
    DPATCH = "dpatch"
    DART = "dart"
    DASM16 = "dasm16"
    DEBCONTROL = "debcontrol"
    DELPHI = "delphi"
    DEVICETREE = "devicetree"
    DG = "dg"
    DIFF = "diff"
    DJANGO = "django"
    DOCKER = "docker"
    DTD = "dtd"
    DUEL = "duel"
    DYLAN_CONSOLE = "dylan-console"
    DYLAN = "dylan"
    DYLAN_LID = "dylan-lid"
    ECL = "ecl"
    EC = "ec"
    EARL_GREY = "earl-grey"
    EASYTRIEVE = "easytrieve"
    EBNF = "ebnf"
    EIFFEL = "eiffel"
    IEX = "iex"
    ELIXIR = "elixir"
    ELM = "elm"
    ELPI = "elpi"
    EMACS_LISP = "emacs-lisp"
    EMAIL = "email"
    ERB = "erb"
    ERLANG = "erlang"
    ERL = "erl"
    HTML_EVOQUE = "html+evoque"
    EVOQUE = "evoque"
    XML_EVOQUE = "xml+evoque"
    EXECLINE = "execline"
    EZHIL = "ezhil"
    FSHARP = "fsharp"
    FSTAR = "fstar"
    FACTOR = "factor"
    FANCY = "fancy"
    FAN = "fan"
    FELIX = "felix"
    FENNEL = "fennel"
    FIFT = "fift"
    FISH = "fish"
    FLATLINE = "flatline"
    FLOSCRIPT = "floscript"
    FORTH = "forth"
    FORTRANFIXED = "fortranfixed"
    FORTRAN = "fortran"
    FOXPRO = "foxpro"
    FREEFEM = "freefem"
    FUNC = "func"
    FUTHARK = "futhark"
    GAP_CONSOLE = "gap-console"
    GAP = "gap"
    GDSCRIPT = "gdscript"
    GLSL = "glsl"
    GSQL = "gsql"
    GAS = "gas"
    GCODE = "gcode"
    GENSHI = "genshi"
    GENSHITEXT = "genshitext"
    POT = "pot"
    GHERKIN = "gherkin"
    GNUPLOT = "gnuplot"
    GO = "go"
    GOLO = "golo"
    GOODDATA_CL = "gooddata-cl"
    GOSU = "gosu"
    GST = "gst"
    GRAPHVIZ = "graphviz"
    GROFF = "groff"
    GROOVY = "groovy"
    HLSL = "hlsl"
    HTML_UL4 = "html+ul4"
    HAML = "haml"
    HTML_HANDLEBARS = "html+handlebars"
    HANDLEBARS = "handlebars"
    HASKELL = "haskell"
    HAXE = "haxe"
    HEXDUMP = "hexdump"
    HSAIL = "hsail"
    HSPEC = "hspec"
    HTML_DJANGO = "html+django"
    HTML_GENSHI = "html+genshi"
    HTML = "html"
    HTML_PHP = "html+php"
    HTML_SMARTY = "html+smarty"
    HTTP = "http"
    HAXEML = "haxeml"
    HYLANG = "hylang"
    HYBRIS = "hybris"
    IDL = "idl"
    ICON = "icon"
    IDRIS = "idris"
    IGOR = "igor"
    INFORM6 = "inform6"
    I6T = "i6t"
    INFORM7 = "inform7"
    INI = "ini"
    IO = "io"
    IOKE = "ioke"
    IRC = "irc"
    ISABELLE = "isabelle"
    J = "j"
    JMESPATH = "jmespath"
    JSLT = "jslt"
    JAGS = "jags"
    JASMIN = "jasmin"
    JAVA = "java"
    JAVASCRIPT_DJANGO = "javascript+django"
    JAVASCRIPT_RUBY = "javascript+ruby"
    JS_GENSHITEXT = "js+genshitext"
    JAVASCRIPT = "javascript"
    JAVASCRIPT_PHP = "javascript+php"
    JAVASCRIPT_SMARTY = "javascript+smarty"
    JS_UL4 = "js+ul4"
    JCL = "jcl"
    JSGF = "jsgf"
    JSONLD = "jsonld"
    JSON = "json"
    JSONNET = "jsonnet"
    JSP = "jsp"
    JLCON = "jlcon"
    JULIA = "julia"
    JUTTLE = "juttle"
    K = "k"
    KAL = "kal"
    KCONFIG = "kconfig"
    KMSG = "kmsg"
    KOKA = "koka"
    KOTLIN = "kotlin"
    KUIN = "kuin"
    LSL = "lsl"
    CSS_LASSO = "css+lasso"
    HTML_LASSO = "html+lasso"
    JAVASCRIPT_LASSO = "javascript+lasso"
    LASSO = "lasso"
    XML_LASSO = "xml+lasso"
    LEAN = "lean"
    LESS = "less"
    LIGHTTPD = "lighttpd"
    LILYPOND = "lilypond"
    LIMBO = "limbo"
    LIQUID = "liquid"
    LITERATE_AGDA = "literate-agda"
    LITERATE_CRYPTOL = "literate-cryptol"
    LITERATE_HASKELL = "literate-haskell"
    LITERATE_IDRIS = "literate-idris"
    LIVESCRIPT = "livescript"
    LLVM = "llvm"
    LLVM_MIR_BODY = "llvm-mir-body"
    LLVM_MIR = "llvm-mir"
    LOGOS = "logos"
    LOGTALK = "logtalk"
    LUA = "lua"
    MCFUNCTION = "mcfunction"
    MCSCHEMA = "mcschema"
    MIME = "mime"
    MIPS = "mips"
    MOOCODE = "moocode"
    DOSCON = "doscon"
    MACAULAY2 = "macaulay2"
    MAKE = "make"
    CSS_MAKO = "css+mako"
    HTML_MAKO = "html+mako"
    JAVASCRIPT_MAKO = "javascript+mako"
    MAKO = "mako"
    XML_MAKO = "xml+mako"
    MAQL = "maql"
    MARKDOWN = "markdown"
    MASK = "mask"
    MASON = "mason"
    MATHEMATICA = "mathematica"
    MATLAB = "matlab"
    MATLABSESSION = "matlabsession"
    MAXIMA = "maxima"
    MESON = "meson"
    MINID = "minid"
    MINISCRIPT = "miniscript"
    MODELICA = "modelica"
    MODULA2 = "modula2"
    TRAC_WIKI = "trac-wiki"
    MONKEY = "monkey"
    MONTE = "monte"
    MOONSCRIPT = "moonscript"
    MOSEL = "mosel"
    CSS_MOZPREPROC = "css+mozpreproc"
    MOZHASHPREPROC = "mozhashpreproc"
    JAVASCRIPT_MOZPREPROC = "javascript+mozpreproc"
    MOZPERCENTPREPROC = "mozpercentpreproc"
    XUL_MOZPREPROC = "xul+mozpreproc"
    MQL = "mql"
    MSCGEN = "mscgen"
    MUPAD = "mupad"
    MXML = "mxml"
    MYSQL = "mysql"
    CSS_MYGHTY = "css+myghty"
    HTML_MYGHTY = "html+myghty"
    JAVASCRIPT_MYGHTY = "javascript+myghty"
    MYGHTY = "myghty"
    XML_MYGHTY = "xml+myghty"
    NCL = "ncl"
    NSIS = "nsis"
    NASM = "nasm"
    OBJDUMP_NASM = "objdump-nasm"
    NEMERLE = "nemerle"
    NESC = "nesc"
    NESTEDTEXT = "nestedtext"
    NEWLISP = "newlisp"
    NEWSPEAK = "newspeak"
    NGINX = "nginx"
    NIMROD = "nimrod"
    NIT = "nit"
    NIXOS = "nixos"
    NODEJSREPL = "nodejsrepl"
    NOTMUCH = "notmuch"
    NUSMV = "nusmv"
    NUMPY = "numpy"
    OBJDUMP = "objdump"
    OBJECTIVE_C = "objective-c"
    OBJECTIVE_C__ = "objective-c++"
    OBJECTIVE_J = "objective-j"
    OCAML = "ocaml"
    OCTAVE = "octave"
    ODIN = "odin"
    OMG_IDL = "omg-idl"
    OOC = "ooc"
    OPA = "opa"
    OPENEDGE = "openedge"
    OUTPUT = "output"
    PACMANCONF = "pacmanconf"
    PAN = "pan"
    PARASAIL = "parasail"
    PAWN = "pawn"
    PEG = "peg"
    PERL6 = "perl6"
    PERL = "perl"
    PHIX = "phix"
    PHP = "php"
    PIG = "pig"
    PIKE = "pike"
    PKGCONFIG = "pkgconfig"
    PLPGSQL = "plpgsql"
    POINTLESS = "pointless"
    PONY = "pony"
    PORTUGOL = "portugol"
    POSTSCRIPT = "postscript"
    PSQL = "psql"
    POSTGRESQL = "postgresql"
    POV = "pov"
    POWERSHELL = "powershell"
    PWSH_SESSION = "pwsh-session"
    PRAAT = "praat"
    PROCFILE = "procfile"
    PROLOG = "prolog"
    PROMQL = "promql"
    PROPERTIES = "properties"
    PROTOBUF = "protobuf"
    PSYSH = "psysh"
    PUG = "pug"
    PUPPET = "puppet"
    PYPYLOG = "pypylog"
    PYTHON2 = "python2"
    PY2TB = "py2tb"
    PYCON = "pycon"
    PYTHON = "python"
    PYTB = "pytb"
    PY_UL4 = "py+ul4"
    QBASIC = "qbasic"
    Q = "q"
    QVTO = "qvto"
    QLIK = "qlik"
    QML = "qml"
    RCONSOLE = "rconsole"
    RNG_COMPACT = "rng-compact"
    SPEC = "spec"
    RACKET = "racket"
    RAGEL_C = "ragel-c"
    RAGEL_CPP = "ragel-cpp"
    RAGEL_D = "ragel-d"
    RAGEL_EM = "ragel-em"
    RAGEL_JAVA = "ragel-java"
    RAGEL = "ragel"
    RAGEL_OBJC = "ragel-objc"
    RAGEL_RUBY = "ragel-ruby"
    RD = "rd"
    REASONML = "reasonml"
    REBOL = "rebol"
    RED = "red"
    REDCODE = "redcode"
    REGISTRY = "registry"
    RESOURCEBUNDLE = "resourcebundle"
    REXX = "rexx"
    RHTML = "rhtml"
    RIDE = "ride"
    RITA = "rita"
    ROBOCONF_GRAPH = "roboconf-graph"
    ROBOCONF_INSTANCES = "roboconf-instances"
    ROBOTFRAMEWORK = "robotframework"
    RQL = "rql"
    RSL = "rsl"
    RESTRUCTUREDTEXT = "restructuredtext"
    TRAFFICSCRIPT = "trafficscript"
    RBCON = "rbcon"
    RUBY = "ruby"
    RUST = "rust"
    SAS = "sas"
    SPLUS = "splus"
    SML = "sml"
    SNBT = "snbt"
    SARL = "sarl"
    SASS = "sass"
    SAVI = "savi"
    SCALA = "scala"
    SCAML = "scaml"
    SCDOC = "scdoc"
    SCHEME = "scheme"
    SCILAB = "scilab"
    SCSS = "scss"
    SED = "sed"
    SHEXC = "shexc"
    SHEN = "shen"
    SIEVE = "sieve"
    SILVER = "silver"
    SINGULARITY = "singularity"
    SLASH = "slash"
    SLIM = "slim"
    SLURM = "slurm"
    SMALI = "smali"
    SMALLTALK = "smalltalk"
    SGF = "sgf"
    SMARTY = "smarty"
    SMITHY = "smithy"
    SNOBOL = "snobol"
    SNOWBALL = "snowball"
    SOLIDITY = "solidity"
    SOPHIA = "sophia"
    SP = "sp"
    DEBSOURCES = "debsources"
    SPARQL = "sparql"
    SPICE = "spice"
    SQL_JINJA = "sql+jinja"
    SQL = "sql"
    SQLITE3 = "sqlite3"
    SQUIDCONF = "squidconf"
    SRCINFO = "srcinfo"
    SSP = "ssp"
    STAN = "stan"
    STATA = "stata"
    SUPERCOLLIDER = "supercollider"
    SWIFT = "swift"
    SWIG = "swig"
    SYSTEMVERILOG = "systemverilog"
    TAP = "tap"
    TNT = "tnt"
    TOML = "toml"
    TADS3 = "tads3"
    TAL = "tal"
    TASM = "tasm"
    TCL = "tcl"
    TCSH = "tcsh"
    TCSHCON = "tcshcon"
    TEA = "tea"
    TEAL = "teal"
    TERATERMMACRO = "teratermmacro"
    TERMCAP = "termcap"
    TERMINFO = "terminfo"
    TERRAFORM = "terraform"
    TEX = "tex"
    TEXT = "text"
    TI = "ti"
    THRIFT = "thrift"
    TID = "tid"
    TLB = "tlb"
    TODOTXT = "todotxt"
    TSQL = "tsql"
    TREETOP = "treetop"
    TURTLE = "turtle"
    HTML_TWIG = "html+twig"
    TWIG = "twig"
    TYPESCRIPT = "typescript"
    TYPOSCRIPTCSSDATA = "typoscriptcssdata"
    TYPOSCRIPTHTMLDATA = "typoscripthtmldata"
    TYPOSCRIPT = "typoscript"
    UL4 = "ul4"
    UCODE = "ucode"
    UNICON = "unicon"
    UNIXCONFIG = "unixconfig"
    URBISCRIPT = "urbiscript"
    USD = "usd"
    VBSCRIPT = "vbscript"
    VCL = "vcl"
    VCLSNIPPETS = "vclsnippets"
    VCTREESTATUS = "vctreestatus"
    VGL = "vgl"
    VALA = "vala"
    ASPX_VB = "aspx-vb"
    VB_NET = "vb.net"
    HTML_VELOCITY = "html+velocity"
    VELOCITY = "velocity"
    XML_VELOCITY = "xml+velocity"
    VERILOG = "verilog"
    VHDL = "vhdl"
    VIM = "vim"
    WDIFF = "wdiff"
    WAST = "wast"
    WEBIDL = "webidl"
    WHILEY = "whiley"
    WOWTOC = "wowtoc"
    WREN = "wren"
    X10 = "x10"
    XML_UL4 = "xml+ul4"
    XQUERY = "xquery"
    XML_DJANGO = "xml+django"
    XML_RUBY = "xml+ruby"
    XML = "xml"
    XML_PHP = "xml+php"
    XML_SMARTY = "xml+smarty"
    XORG_CONF = "xorg.conf"
    XSLT = "xslt"
    XTEND = "xtend"
    EXTEMPORE = "extempore"
    YAML_JINJA = "yaml+jinja"
    YAML = "yaml"
    YANG = "yang"
    ZEEK = "zeek"
    ZEPHIR = "zephir"
    ZIG = "zig"
    ANSYS = "ansys"
    IPYTHON2 = "ipython2"
    IPYTHON3 = "ipython3"
    IPYTHONCONSOLE = "ipythonconsole"

Image Viewer

Bases: BaseEditor

ImageViewer class.

Parameters:

Name Type Description Default
master

Parent widget.

required
path str

Path to the image file.

required
Source code in cupcake\imageviewer\__init__.py
class ImageViewer(BaseEditor):
    """ImageViewer class.

    Args:
        master: Parent widget.
        path: Path to the image file.
    """

    def __init__(self, master, path: str, *args, **kwargs):
        super().__init__(master, path, editable=False, *args, **kwargs)
        self.open_image()

    def open_image(self):
        """Opens the image file."""
        self.image = Image.open(self.path)
        self.image.thumbnail((700, 700))
        self.tk_image = ImageTk.PhotoImage(self.image)

        self.image_label = tk.Label(
            self, image=self.tk_image, bg=self.base.theme.background
        )
        self.image_label.pack(fill=tk.BOTH, expand=True)

open_image()

Opens the image file.

Source code in cupcake\imageviewer\__init__.py
def open_image(self):
    """Opens the image file."""
    self.image = Image.open(self.path)
    self.image.thumbnail((700, 700))
    self.tk_image = ImageTk.PhotoImage(self.image)

    self.image_label = tk.Label(
        self, image=self.tk_image, bg=self.base.theme.background
    )
    self.image_label.pack(fill=tk.BOTH, expand=True)

Text Editor

Bases: BaseEditor

TextEditor class.

Parameters:

Name Type Description Default
master

Parent widget.

required
path

Path to the file.

''
language

Language to use for syntax highlighting.

''
font

Font to use for the text.

''
minimalist

Whether to use the minimalist mode.

False
Source code in cupcake\texteditor\__init__.py
class TextEditor(BaseEditor):
    """TextEditor class.

    Args:
        master: Parent widget.
        path: Path to the file.
        language: Language to use for syntax highlighting.
        font: Font to use for the text.
        minimalist: Whether to use the minimalist mode.
    """

    def __init__(
        self, master, path="", language="", font="", minimalist=False, *args, **kwargs
    ):
        super().__init__(master, path, *args, **kwargs)
        self.font = font or self.base.settings.font
        self.minimalist = minimalist
        self.language = language

        self.rowconfigure(0, weight=1)
        self.columnconfigure(1, weight=1)

        self.text = Text(self, path=self.path, minimalist=minimalist, language=language)
        self.linenumbers = LineNumbers(self, self.text, self.font)
        self.scrollbar = Scrollbar(
            self, orient=tk.VERTICAL, command=self.text.yview, style="EditorScrollbar"
        )

        self.text.config(font=self.font)
        self.text.configure(yscrollcommand=self.scrollbar.set)

        if not self.minimalist:
            self.minimap = Minimap(self, self.text)
            self.minimap.grid(row=0, column=2, sticky=tk.NS)

        self.linenumbers.grid(row=0, column=0, sticky=tk.NS)
        self.text.grid(row=0, column=1, sticky=tk.NSEW)
        self.scrollbar.grid(row=0, column=3, sticky=tk.NS)

        self.text.bind("<<Change>>", self.on_change)
        self.text.bind("<<Scroll>>", self.on_scroll)

        if self.path and os.path.isfile(self.path):
            self.text.load_file()

    def on_change(self, *_):
        self.text.refresh()
        self.linenumbers.redraw()
        # self.minimap.redraw()

    def on_scroll(self, *_):
        self.linenumbers.redraw()

    def unsupported_file(self):
        self.text.highlighter.lexer = None
        self.text.show_unsupported_dialog()
        self.linenumbers.grid_remove()
        self.scrollbar.grid_remove()
        self.editable = False

    def focus(self):
        self.text.focus()
        self.on_change()

    def set_fontsize(self, size):
        self.font.configure(size=size)
        self.linenumbers.set_bar_width(size * 3)
        self.on_change()

    def save(self, path=None):
        if self.editable:
            self.text.save_file(path)

    def cut(self, *_):
        if self.editable:
            self.text.cut()

    def copy(self, *_):
        if self.editable:
            self.text.copy()

    def paste(self, *_):
        if self.editable:
            self.text.paste()

    def write(self, *args, **kwargs):
        if self.editable:
            self.text.write(*args, **kwargs)

    def insert(self, *args, **kwargs):
        if self.editable:
            self.text.insert(*args, **kwargs)

    def get(self, *args, **kwargs):
        if self.editable:
            self.text.get(*args, **kwargs)

    def clear(self):
        self.delete("1.0", tk.END)

    def delete(self, *args, **kwargs):
        if self.editable:
            self.text.delete(*args, **kwargs)

    def mark_set(self, *args, **kwargs):
        if self.editable:
            self.text.mark_set(*args, **kwargs)

    def compare(self, *args, **kwargs):
        return self.text.compare(*args, **kwargs)

    def dlineinfo(self, index):
        return self.text.dlineinfo(index)

    def edit_modified(self, arg=None):
        return self.text.edit_modified(arg)

    def edit_redo(self):
        if self.editable:
            self.text.edit_redo()

    def edit_reset(self):
        if self.editable:
            self.text.edit_reset()

    def edit_separator(self):
        if self.editable:
            self.text.edit_separator()

    def edit_undo(self):
        if self.editable:
            self.text.edit_undo()

    def image_create(self, index, **kwargs):
        if self.editable:
            return self.text.image_create(index, **kwargs)

    def image_cget(self, index, option):
        return self.text.image_cget(index, option)

    def image_configure(self, index, **kwargs):
        if self.editable:
            return self.text.image_configure(index, **kwargs)

    def image_names(self):
        return self.text.image_names()

    def index(self, i):
        return self.text.index(i)

    def mark_gravity(self, mark, gravity=None):
        return self.text.mark_gravity(mark, gravity)

    def mark_names(self):
        return self.text.mark_names()

    def mark_next(self, index):
        return self.text.mark_next(index)

    def mark_previous(self, index):
        return self.text.mark_previous(index)

    def mark_set(self, mark, index):
        if self.editable:
            self.text.mark_set(mark, index)

    def mark_unset(self, mark):
        if self.editable:
            self.text.mark_unset(mark)

    def scan_dragto(self, x, y):
        self.text.scan_dragto(x, y)

    def scan_mark(self, x, y):
        self.text.scan_mark(x, y)

    def search(self, pattern, index, **kwargs):
        return self.text.search(pattern, index, **kwargs)

    def see(self, index):
        self.text.see(index)

    def tag_add(self, tagName, index1, index2=None):
        if self.editable:
            self.text.tag_add(tagName, index1, index2)

    def tag_bind(self, tagName, sequence, func, add=None):
        self.text.tag_bind(tagName, sequence, func, add)

    def tag_cget(self, tagName, option):
        return self.text.tag_cget(tagName, option)

    def tag_config(self, tagName, **kwargs):
        if self.editable:
            self.text.tag_config(tagName, **kwargs)

    def tag_names(self, index=None):
        return self.text.tag_names(index)

    def tag_nextrange(self, tagName, index1, index2=None):
        return self.text.tag_nextrange(tagName, index1, index2)

    def tag_prevrange(self, tagName, index1, index2=None):
        return self.text.tag_prevrange(tagName, index1, index2)

    def tag_raise(self, tagName, aboveThis=None):
        if self.editable:
            self.text.tag_raise(tagName, aboveThis)

    def tag_ranges(self, tagName):
        return self.text.tag_ranges(tagName)

    def tag_remove(self, tagName, index1, index2=None):
        if self.editable:
            self.text.tag_remove(tagName, index1, index2)

    def tag_unbind(self, tagName, sequence, funcid=None):
        self.text.tag_unbind(tagName, sequence, funcid)

    def window_cget(self, index, option):
        return self.text.window_cget(index, option)

    def window_configure(self, index, **kwargs):
        if self.editable:
            self.text.window_configure(index, **kwargs)

    def window_create(self, index, **kwargs):
        if self.editable:
            self.text.window_create(index, **kwargs)

    def window_names(self):
        return self.text.window_names()

    def xview_moveto(self, fraction):
        self.text.xview_moveto(fraction)

    def xview_scroll(self, n, what):
        self.text.xview_scroll(n, what)

    def yview_moveto(self, fraction):
        self.text.yview_moveto(fraction)

    def yview_scroll(self, n, what):
        self.text.yview_scroll(n, what)

Bases: Frame

BreadCrumbs class.

Parameters:

Name Type Description Default
master

Parent widget.

required
path

Path to the file.

''

Attributes:

Name Type Description
pathview

PathView object.

Source code in cupcake\breadcrumbs\__init__.py
class BreadCrumbs(Frame):
    """BreadCrumbs class.

    Args:
        master: Parent widget.
        path: Path to the file.

    Attributes:
        pathview: PathView object.
    """

    def __init__(self, master, path="", *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.config(padx=20, bg=self.base.theme.breadcrumbs["background"])

        self.pathview = PathView(self)
        path = os.path.abspath(path).split("\\")
        for i, item in enumerate(path):
            text = item if item == path[-1] else f"{item} ›"
            self.additem("\\".join(path[:i]), text)

    def additem(self, path: str, text: str):
        """Adds an item to the breadcrumbs.

        Args:
            path: Path to the item.
            text: Text to display.
        """

        btn = Item(self, path, text=text)
        btn.bind("<Button-1>", self.pathview.show)
        btn.pack(side=tk.LEFT)

additem(path, text)

Adds an item to the breadcrumbs.

Parameters:

Name Type Description Default
path str

Path to the item.

required
text str

Text to display.

required
Source code in cupcake\breadcrumbs\__init__.py
def additem(self, path: str, text: str):
    """Adds an item to the breadcrumbs.

    Args:
        path: Path to the item.
        text: Text to display.
    """

    btn = Item(self, path, text=text)
    btn.bind("<Button-1>", self.pathview.show)
    btn.pack(side=tk.LEFT)

Path View

Bases: Toplevel

PathView class.

Parameters:

Name Type Description Default
master

Parent widget.

required
width

Width of the path view.

150
Source code in cupcake\breadcrumbs\pathview.py
class PathView(Toplevel):
    """PathView class.

    Args:
        master: Parent widget.
        width: Width of the path view.
    """

    def __init__(self, master, width=150, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.width = round(width)

        self.tree = DirectoryTree(self, width=width)
        self.tree.pack()

        self.config(pady=1, padx=1, bg=self.base.theme.border)
        self.overrideredirect(True)
        self.withdraw()

        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(0, weight=1)
        self.configure_bindings()

    def configure_bindings(self):
        self.bind("<FocusOut>", self.hide)
        self.bind("<Escape>", self.hide)

    def get_popup_x(self, width):
        """Returns the x position for the popup.

        Args:
            width: Width of the popup.

        Returns:
            int: X position.
        """

        return self.winfo_rootx() + int(self.winfo_width() / 2) - int(width / 2)

    def get_popup_y(self):
        """Returns the y position for the popup.

        Returns:
            int: Y position.
        """

        return self.winfo_rooty()

    def hide(self, *_):
        """Hides the path view."""

        self.withdraw()

    def show(self, e: tk.Event):
        """Shows the path view.

        Args:
            e: Event object.
        """

        self.update_idletasks()
        w = e.widget
        x = w.winfo_rootx()
        y = w.winfo_rooty() + w.winfo_height()

        if not w.path:
            return
        self.tree.change_path(w.path)

        self.geometry(f"+{x}+{y}")
        self.deiconify()
        self.focus_set()

get_popup_x(width)

Returns the x position for the popup.

Parameters:

Name Type Description Default
width

Width of the popup.

required

Returns:

Name Type Description
int

X position.

Source code in cupcake\breadcrumbs\pathview.py
def get_popup_x(self, width):
    """Returns the x position for the popup.

    Args:
        width: Width of the popup.

    Returns:
        int: X position.
    """

    return self.winfo_rootx() + int(self.winfo_width() / 2) - int(width / 2)

get_popup_y()

Returns the y position for the popup.

Returns:

Name Type Description
int

Y position.

Source code in cupcake\breadcrumbs\pathview.py
def get_popup_y(self):
    """Returns the y position for the popup.

    Returns:
        int: Y position.
    """

    return self.winfo_rooty()

hide(*_)

Hides the path view.

Source code in cupcake\breadcrumbs\pathview.py
def hide(self, *_):
    """Hides the path view."""

    self.withdraw()

show(e)

Shows the path view.

Parameters:

Name Type Description Default
e Event

Event object.

required
Source code in cupcake\breadcrumbs\pathview.py
def show(self, e: tk.Event):
    """Shows the path view.

    Args:
        e: Event object.
    """

    self.update_idletasks()
    w = e.widget
    x = w.winfo_rootx()
    y = w.winfo_rooty() + w.winfo_height()

    if not w.path:
        return
    self.tree.change_path(w.path)

    self.geometry(f"+{x}+{y}")
    self.deiconify()
    self.focus_set()

Line Numbers

Bases: Canvas

Source code in cupcake\texteditor\linenumbers\__init__.py
class LineNumbers(Canvas):
    def __init__(self, master, text=None, font=None, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.font = font

        self.fg = self.base.theme.linenumbers["foreground"]
        self.hfg = self.base.theme.linenumbers["activeforeground"]

        self.config(width=65, bd=0, highlightthickness=0, bg=self.base.theme.linenumbers["background"])
        self.text = text

    def attach(self, text):
        self.text = text

    def mark_line(self, line):
        dline = self.text.dlineinfo(line)

        if not dline:
            return

        y = dline[1]
        btn = Menubutton(self, 
            text=">", font=self.font, cursor="hand2", borderwidth=0,
            width=2, height=1, pady=0, padx=0, relief=tk.FLAT, **self.base.theme.linenumbers)
        self.create_window(70, y-2, anchor=tk.NE, window=btn)

    def set_bar_width(self, width):
        self.configure(width=width)

    def get_indentation_level(self, line):
        """Get the indentation level of a given line."""
        return len(line) - len(line.lstrip())

    def redraw(self, *_):
        self.delete(tk.ALL)

        prev_indent = 0
        i = self.text.index("@0,0")
        while True:
            dline = self.text.dlineinfo(i)
            if dline is None:
                break

            y = dline[1]
            linenum = str(i).split(".")[0]

            # Get the text content of the current line
            line_content = self.text.get(f"{linenum}.0", f"{linenum}.end")
            current_indent = self.get_indentation_level(line_content)

            # to highlight the current line
            curline = self.text.dlineinfo(tk.INSERT)
            cur_y = curline[1] if curline else None

            if not cur_y:
                i = self.text.index(f"{i}+1line")
                continue

            self.create_text(40, y, anchor=tk.NE, text=linenum, font=self.font, tag=i, fill=self.hfg if y == cur_y else self.fg)
            self.tag_bind(i, "<Button-1>", lambda _, i=i: self.text.select_line(i))

            if current_indent > prev_indent:
                self.create_text(50, y, anchor=tk.NW, text="+", font=self.font, fill=self.fg, tag=f"f{i}")
                self.tag_bind(f"f{i}", "<Button-1>", lambda _, i=i: print(f"Fold from {i}"))

            # Update the previous indentation level
            prev_indent = current_indent
            i = self.text.index(f"{i}+1line")

    def draw_breakpoint(self, y):
        bp = Breakpoint(self)
        self.create_window(21, y-2, anchor=tk.NE, window=bp)

get_indentation_level(line)

Get the indentation level of a given line.

Source code in cupcake\texteditor\linenumbers\__init__.py
def get_indentation_level(self, line):
    """Get the indentation level of a given line."""
    return len(line) - len(line.lstrip())

Auto Complete

Bases: Toplevel

AutoComplete class.

Parameters:

Name Type Description Default
master

Parent widget.

required
items

List of items to autocomplete.

None
active

Whether the autocomplete is active.

False
Source code in cupcake\texteditor\autocomplete\__init__.py
class AutoComplete(Toplevel):
    """AutoComplete class.

    Args:
        master: Parent widget.
        items: List of items to autocomplete.
        active: Whether the autocomplete is active.
    """

    def __init__(self, master, items=None, active=False, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.autocomplete_kinds = Kinds(self)
        self.config(padx=1, pady=1, bg=self.base.theme.border)

        self.active = active
        if not self.active:
            self.withdraw()

        self.overrideredirect(True)
        self.wm_attributes("-topmost", True)
        self.grid_columnconfigure(0, weight=1)

        self.menu_items = []
        self.active_items = []
        self.row = 0
        self.selected = 0
        if items:
            # TODO this should be a dict
            self.items = items  # [(completion, type), ...]
            self.add_all_items()
            self.refresh_selected()

    def update_completions(self):
        """Updates and rerenders the completions."""

        self.refresh_geometry()
        self.update_idletasks()
        self.update_all_words()

        term = self.master.get_current_word()

        exact, starts, includes = [], [], []
        for i in self.menu_items:
            if i.get_text() == term:
                exact.append(i)
            elif i.get_text().startswith(term):
                starts.append(i)
            elif term in i.get_text():
                includes.append(i)
        new = list(chain(exact, starts, includes))

        self.hide_all_items()
        if any(new):
            self.show_items(new[:10] if len(new) > 10 else new, term)
        else:
            self.hide()

    def move_up(self, *_):
        """Moves the selection up."""
        if self.active:
            self.select(-1)
            return "break"

    def move_down(self, *_):
        """Moves the selection down."""
        if self.active:
            self.select(1)
            return "break"

    def add_all_items(self):
        """Adds all items to the menu."""
        for i in self.items:
            self.add_item(i[0], i[1] if len(i) > 1 else None)

        self.active_items = self.menu_items
        self.refresh_selected()

    def update_all_words(self):
        """Updates the words in the menu."""
        for word in self.master.words:
            if word not in self.get_items_text():
                self.add_item(word, "word")

        for word in self.menu_items:
            if word.get_text() not in self.master.words and word.get_kind() == "word":
                self.remove_item(word)

    def add_item(self, text: str, kind=""):
        """Adds an item to the menu.

        Args:
            text: Text to add.
            kind: Kind of the item.
        """

        new_item = AutoCompleteItem(self, text, kind=kind)
        new_item.grid(row=self.row, sticky=tk.EW)

        self.menu_items.append(new_item)

        self.row += 1

    def remove_item(self, item: AutoCompleteItem):
        """Removes an item from the menu.

        Args:
            item: Item to remove.
        """

        a = self.menu_items
        item.grid_forget()
        self.menu_items.remove(item)
        self.row -= 1

    def select(self, delta: int):
        """Selects an item.

        Args:
            delta: The change in selection.
        """

        self.selected += delta
        if self.selected > len(self.active_items) - 1:
            self.selected = 0
        elif self.selected < 0:
            self.selected = len(self.active_items) - 1
        self.refresh_selected()

    def reset_selection(self):
        """Resets the selection."""

        self.selected = 0
        self.refresh_selected()

    def refresh_selected(self):
        """Refreshes the selected item."""

        for i in self.active_items:
            i.deselect()
        if self.selected < len(self.active_items):
            self.active_items[self.selected].select()

    def get_items_text(self):
        """Gets the text of all items.

        Returns:
            List of text of all items.
        """

        return [i.get_text() for i in self.menu_items]

    def hide_all_items(self):
        """Hides all items."""

        for i in self.menu_items:
            i.grid_forget()

        self.active_items = []
        self.row = 1

    def show_items(self, items: list[AutoCompleteItem], term: str):
        """Shows the items.

        Args:
            items: Items to show.
            term: The term to match.
        """

        self.active_items = items
        for i in items:
            i.grid(row=self.row, sticky=tk.EW)
            self.row += 1

            i.mark_term(term)

        self.reset_selection()

    def refresh_geometry(self, *_):
        """Refreshes the geometry of the menu."""

        self.update_idletasks()
        self.geometry("+{}+{}".format(*self.master.cursor_screen_location()))

    def show(self, pos: tuple[int, int]):
        """Shows the menu.

        Args:
            pos: Position to show the menu.
        """

        self.active = True
        self.update_idletasks()
        self.geometry("+{}+{}".format(*pos))
        self.deiconify()

    def hide(self, *_):
        """Hides the menu."""

        self.active = False
        self.withdraw()
        self.reset()

    def reset(self):
        """Resets the menu."""

        self.reset_selection()

    def choose(self, this=None, *_):
        """Chooses an item.

        Args:
            this: The item to choose. Used when user clicks on an item.
        """

        if not self.active_items:
            return

        if not this:
            this = self.active_items[self.selected]

        self.master.confirm_autocomplete(this.get_text())
        self.hide()
        return "break"

add_all_items()

Adds all items to the menu.

Source code in cupcake\texteditor\autocomplete\__init__.py
def add_all_items(self):
    """Adds all items to the menu."""
    for i in self.items:
        self.add_item(i[0], i[1] if len(i) > 1 else None)

    self.active_items = self.menu_items
    self.refresh_selected()

add_item(text, kind='')

Adds an item to the menu.

Parameters:

Name Type Description Default
text str

Text to add.

required
kind

Kind of the item.

''
Source code in cupcake\texteditor\autocomplete\__init__.py
def add_item(self, text: str, kind=""):
    """Adds an item to the menu.

    Args:
        text: Text to add.
        kind: Kind of the item.
    """

    new_item = AutoCompleteItem(self, text, kind=kind)
    new_item.grid(row=self.row, sticky=tk.EW)

    self.menu_items.append(new_item)

    self.row += 1

choose(this=None, *_)

Chooses an item.

Parameters:

Name Type Description Default
this

The item to choose. Used when user clicks on an item.

None
Source code in cupcake\texteditor\autocomplete\__init__.py
def choose(self, this=None, *_):
    """Chooses an item.

    Args:
        this: The item to choose. Used when user clicks on an item.
    """

    if not self.active_items:
        return

    if not this:
        this = self.active_items[self.selected]

    self.master.confirm_autocomplete(this.get_text())
    self.hide()
    return "break"

get_items_text()

Gets the text of all items.

Returns:

Type Description

List of text of all items.

Source code in cupcake\texteditor\autocomplete\__init__.py
def get_items_text(self):
    """Gets the text of all items.

    Returns:
        List of text of all items.
    """

    return [i.get_text() for i in self.menu_items]

hide(*_)

Hides the menu.

Source code in cupcake\texteditor\autocomplete\__init__.py
def hide(self, *_):
    """Hides the menu."""

    self.active = False
    self.withdraw()
    self.reset()

hide_all_items()

Hides all items.

Source code in cupcake\texteditor\autocomplete\__init__.py
def hide_all_items(self):
    """Hides all items."""

    for i in self.menu_items:
        i.grid_forget()

    self.active_items = []
    self.row = 1

move_down(*_)

Moves the selection down.

Source code in cupcake\texteditor\autocomplete\__init__.py
def move_down(self, *_):
    """Moves the selection down."""
    if self.active:
        self.select(1)
        return "break"

move_up(*_)

Moves the selection up.

Source code in cupcake\texteditor\autocomplete\__init__.py
def move_up(self, *_):
    """Moves the selection up."""
    if self.active:
        self.select(-1)
        return "break"

refresh_geometry(*_)

Refreshes the geometry of the menu.

Source code in cupcake\texteditor\autocomplete\__init__.py
def refresh_geometry(self, *_):
    """Refreshes the geometry of the menu."""

    self.update_idletasks()
    self.geometry("+{}+{}".format(*self.master.cursor_screen_location()))

refresh_selected()

Refreshes the selected item.

Source code in cupcake\texteditor\autocomplete\__init__.py
def refresh_selected(self):
    """Refreshes the selected item."""

    for i in self.active_items:
        i.deselect()
    if self.selected < len(self.active_items):
        self.active_items[self.selected].select()

remove_item(item)

Removes an item from the menu.

Parameters:

Name Type Description Default
item AutoCompleteItem

Item to remove.

required
Source code in cupcake\texteditor\autocomplete\__init__.py
def remove_item(self, item: AutoCompleteItem):
    """Removes an item from the menu.

    Args:
        item: Item to remove.
    """

    a = self.menu_items
    item.grid_forget()
    self.menu_items.remove(item)
    self.row -= 1

reset()

Resets the menu.

Source code in cupcake\texteditor\autocomplete\__init__.py
def reset(self):
    """Resets the menu."""

    self.reset_selection()

reset_selection()

Resets the selection.

Source code in cupcake\texteditor\autocomplete\__init__.py
def reset_selection(self):
    """Resets the selection."""

    self.selected = 0
    self.refresh_selected()

select(delta)

Selects an item.

Parameters:

Name Type Description Default
delta int

The change in selection.

required
Source code in cupcake\texteditor\autocomplete\__init__.py
def select(self, delta: int):
    """Selects an item.

    Args:
        delta: The change in selection.
    """

    self.selected += delta
    if self.selected > len(self.active_items) - 1:
        self.selected = 0
    elif self.selected < 0:
        self.selected = len(self.active_items) - 1
    self.refresh_selected()

show(pos)

Shows the menu.

Parameters:

Name Type Description Default
pos tuple[int, int]

Position to show the menu.

required
Source code in cupcake\texteditor\autocomplete\__init__.py
def show(self, pos: tuple[int, int]):
    """Shows the menu.

    Args:
        pos: Position to show the menu.
    """

    self.active = True
    self.update_idletasks()
    self.geometry("+{}+{}".format(*pos))
    self.deiconify()

show_items(items, term)

Shows the items.

Parameters:

Name Type Description Default
items list[AutoCompleteItem]

Items to show.

required
term str

The term to match.

required
Source code in cupcake\texteditor\autocomplete\__init__.py
def show_items(self, items: list[AutoCompleteItem], term: str):
    """Shows the items.

    Args:
        items: Items to show.
        term: The term to match.
    """

    self.active_items = items
    for i in items:
        i.grid(row=self.row, sticky=tk.EW)
        self.row += 1

        i.mark_term(term)

    self.reset_selection()

update_all_words()

Updates the words in the menu.

Source code in cupcake\texteditor\autocomplete\__init__.py
def update_all_words(self):
    """Updates the words in the menu."""
    for word in self.master.words:
        if word not in self.get_items_text():
            self.add_item(word, "word")

    for word in self.menu_items:
        if word.get_text() not in self.master.words and word.get_kind() == "word":
            self.remove_item(word)

update_completions()

Updates and rerenders the completions.

Source code in cupcake\texteditor\autocomplete\__init__.py
def update_completions(self):
    """Updates and rerenders the completions."""

    self.refresh_geometry()
    self.update_idletasks()
    self.update_all_words()

    term = self.master.get_current_word()

    exact, starts, includes = [], [], []
    for i in self.menu_items:
        if i.get_text() == term:
            exact.append(i)
        elif i.get_text().startswith(term):
            starts.append(i)
        elif term in i.get_text():
            includes.append(i)
    new = list(chain(exact, starts, includes))

    self.hide_all_items()
    if any(new):
        self.show_items(new[:10] if len(new) > 10 else new, term)
    else:
        self.hide()