| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681 |
- # Part of ImGui Bundle - MIT License - Copyright (c) 2022-2026 Pascal Thomet - https://github.com/pthom/imgui_bundle
- import inspect
- import textwrap
- from typing import Callable, Any
- from imgui_bundle import imgui, imgui_color_text_edit as ed, imgui_md, ImVec2
- from imgui_bundle.immapp import static
- TextEditor = ed.TextEditor
- TextDiff = ed.TextDiff
- # ============================================================================
- # Source display helper: shows the code of a demo function in a collapsible section
- # ============================================================================
- _source_show_flags: dict[str, bool] = {}
- _source_editors: dict[str, TextEditor] = {}
- # Map function names to their actual function objects (filled by each demo function)
- _source_functions: dict[str, Callable[..., Any]] = {}
- def _show_source_toggle(func: Callable[..., Any]) -> None:
- """Show a 'Show source' checkbox. When checked, displays the function's source
- (obtained via inspect.getsource) in a read-only editor with light theme."""
- func_name = func.__name__
- if func_name not in _source_show_flags:
- _source_show_flags[func_name] = False
- _, _source_show_flags[func_name] = imgui.checkbox("Show source", _source_show_flags[func_name])
- if _source_show_flags[func_name]:
- imgui.separator_text("Source")
- if func_name not in _source_editors:
- source = textwrap.dedent(inspect.getsource(func))
- editor = TextEditor()
- editor.set_text(source)
- editor.set_language(TextEditor.Language.python())
- editor.set_palette(TextEditor.get_light_palette())
- editor.set_read_only_enabled(True)
- editor.set_carets_visible(False)
- _source_editors[func_name] = editor
- code_font = imgui_md.get_code_font()
- imgui.push_font(code_font.font, code_font.size)
- _source_editors[func_name].render(f"##src_{func_name}", ImVec2(-1, imgui.get_text_line_height() * 15), False)
- imgui.pop_font()
- imgui.separator_text("Demo")
- # ============================================================================
- # Tab 1: Basic Editor
- # Demonstrates: text loading, language selection, palette switching
- # ============================================================================
- @static(initialized=False, editor=None, lang_idx=3) # default: Python
- def demo_basic_editor():
- _show_source_toggle(demo_basic_editor)
- statics = demo_basic_editor
- if not statics.initialized:
- statics.editor = TextEditor()
- with open(__file__, encoding="utf8") as f:
- code = f.read()
- statics.editor.set_text(code)
- statics.editor.set_language(TextEditor.Language.python())
- statics.initialized = True
- editor = statics.editor
- # Palette buttons
- if imgui.small_button("Dark"):
- editor.set_palette(TextEditor.get_dark_palette())
- imgui.same_line()
- if imgui.small_button("Light"):
- editor.set_palette(TextEditor.get_light_palette())
- # Language selection
- imgui.same_line()
- imgui.set_next_item_width(imgui.calc_text_size("AngelScript__").x)
- lang_names = ["None", "C++", "C", "Python", "GLSL", "HLSL", "Lua", "SQL", "AngelScript", "C#", "JSON", "Markdown"]
- changed, statics.lang_idx = imgui.combo("Language", statics.lang_idx, lang_names)
- if changed:
- langs = [
- None,
- TextEditor.Language.cpp(), TextEditor.Language.c(),
- TextEditor.Language.python(), TextEditor.Language.glsl(),
- TextEditor.Language.hlsl(), TextEditor.Language.lua(),
- TextEditor.Language.sql(), TextEditor.Language.angel_script(),
- TextEditor.Language.cs(), TextEditor.Language.json(),
- TextEditor.Language.markdown(),
- ]
- editor.set_language(langs[statics.lang_idx])
- # Cursor position display (doc coords: row != line once word-wrap is on)
- imgui.same_line()
- pos = editor.get_main_cursor_position()
- imgui.text(f"Line: {pos.line + 1} Col: {pos.index + 1} (doc)")
- # Second row: editor view/behavior toggles
- wrap = editor.is_word_wrap_enabled()
- changed, wrap = imgui.checkbox("Word Wrap", wrap)
- if changed:
- editor.set_word_wrap_enabled(wrap)
- imgui.same_line()
- read_only = editor.is_read_only_enabled()
- changed, read_only = imgui.checkbox("Read Only", read_only)
- if changed:
- editor.set_read_only_enabled(read_only)
- imgui.same_line()
- folding = editor.is_line_folding_enabled()
- changed, folding = imgui.checkbox("Line Folding", folding)
- if changed:
- editor.set_line_folding_enabled(folding)
- imgui.same_line()
- imgui.begin_disabled(not folding)
- if imgui.small_button("Unfold All"):
- editor.unfold_all()
- imgui.end_disabled()
- imgui.same_line()
- mini_map = editor.is_show_mini_map_enabled()
- changed, mini_map = imgui.checkbox("Show Mini Map", mini_map)
- if changed:
- editor.set_show_mini_map_enabled(mini_map)
- imgui.text_disabled("(folding uses brackets for C/C++, indentation for Python)")
- # Render editor: we shall use a monospace font
- code_font = imgui_md.get_code_font()
- imgui.push_font(code_font.font, code_font.size)
- editor.render("##basic")
- imgui.pop_font()
- # ============================================================================
- # Tab 2: Change Callback
- # Demonstrates: detecting edits via set_change_callback
- # ============================================================================
- @static(initialized=False, editor=None, change_count=0)
- def demo_change_callback():
- _show_source_toggle(demo_change_callback)
- statics = demo_change_callback
- if not statics.initialized:
- statics.editor = TextEditor()
- statics.editor.set_text("Edit this text to see the change callback in action.\n\nTry typing, deleting, or pasting.\n")
- statics.editor.set_language(TextEditor.Language.python())
- statics.editor.set_change_callback(lambda: setattr(statics, 'change_count', statics.change_count + 1), 200)
- statics.initialized = True
- editor = statics.editor
- imgui.text(f"Change count: {statics.change_count}")
- code_font = imgui_md.get_code_font()
- imgui.push_font(code_font.font, code_font.size)
- editor.render("##changes")
- imgui.pop_font()
- # ============================================================================
- # Tab 3: Filters
- # Demonstrates: filter_selections to transform selected text
- # ============================================================================
- @static(initialized=False, editor=None)
- def demo_filters():
- _show_source_toggle(demo_filters)
- statics = demo_filters
- if not statics.initialized:
- statics.editor = TextEditor()
- statics.editor.set_text(
- "Select some text below, then click a filter button.\n"
- "\n"
- "Hello World\n"
- "the quick brown fox jumps over the lazy dog\n"
- "SOME UPPERCASE TEXT\n"
- "mixed Case Text Here\n"
- )
- statics.initialized = True
- editor = statics.editor
- imgui.text("Select text, then apply a filter:")
- imgui.same_line()
- if imgui.small_button("UPPER"):
- editor.filter_selections(lambda text: text.upper())
- imgui.same_line()
- if imgui.small_button("lower"):
- editor.filter_selections(lambda text: text.lower())
- imgui.same_line()
- if imgui.small_button("Strip trailing spaces"):
- editor.strip_trailing_whitespaces()
- code_font = imgui_md.get_code_font()
- imgui.push_font(code_font.font, code_font.size)
- editor.render("##filters")
- imgui.pop_font()
- # ============================================================================
- # Tab 4: Annotations & Interactivity
- # Demonstrates four ways to annotate or react inside an editor:
- # - Line decorator (custom-drawn gutter): red circle on breakpoint lines
- # - Line markers (colored gutter + tooltips): error & warning markers
- # - Context menus on line numbers and on text (right-click)
- # - Hover callback in text: live popup showing the word under the cursor
- # ============================================================================
- @static(initialized=False, editor=None, breakpoints=None, last_action="")
- def demo_decorators_and_context_menus():
- _show_source_toggle(demo_decorators_and_context_menus)
- statics = demo_decorators_and_context_menus
- if not statics.initialized:
- statics.breakpoints = set()
- statics.editor = TextEditor()
- statics.editor.set_text(
- "import math\n"
- "from typing import List\n"
- "\n"
- "# Compute summary statistics for a list of numbers.\n"
- "def stats(values):\n"
- " if not values:\n"
- " return None\n"
- " total = sum(values)\n"
- " count = len(values)\n"
- " mean = total / count\n"
- " variance = sum((v - mean) ** 2 for v in values) / count\n"
- " unused = 'leftover'\n"
- " return mean, math.sqrt(variance)\n"
- "\n"
- "def greet(name):\n"
- ' print(f"Hello {name}")\n'
- " return name\n"
- "\n"
- "def divide(a, b):\n"
- " return a // b\n"
- "\n"
- "def main():\n"
- " data = [1, 2, 3, 4, 5]\n"
- " m, s = stats(data)\n"
- ' print(f"mean={m} std={s}")\n'
- ' greet("world")\n'
- " print(divide(10, 0))\n"
- "\n"
- 'if __name__ == "__main__":\n'
- " main()\n"
- )
- statics.editor.set_language(TextEditor.Language.python())
- # Markers: persistent colored bands in the gutter / text background,
- # with built-in tooltips on hover (no callback needed for the tooltip).
- # Spread across the file so the scrollbar mini map has visible ticks.
- # Line numbers are zero-based.
- warning_color = imgui.IM_COL32(180, 140, 0, 255)
- warning_bg = imgui.IM_COL32(180, 140, 0, 48)
- error_color = imgui.IM_COL32(220, 50, 50, 255)
- error_bg = imgui.IM_COL32(220, 50, 50, 56)
- statics.editor.add_marker(
- 1, warning_color, warning_bg,
- "warning", "Imported but never used: 'List'",
- )
- statics.editor.add_marker(
- 11, warning_color, warning_bg,
- "warning", "Unused variable: 'unused'",
- )
- statics.editor.add_marker(
- 19, error_color, error_bg,
- "error", "Unchecked division: b may be zero",
- )
- statics.editor.add_marker(
- 26, error_color, error_bg,
- "error", "ZeroDivisionError at runtime: divide(10, 0)",
- )
- # Decorator: draw a red circle on lines that have a breakpoint
- def decorator_callback(decorator: TextEditor.Decorator):
- if decorator.line in statics.breakpoints:
- cursor_pos = imgui.get_cursor_screen_pos()
- center = ImVec2(
- cursor_pos.x + decorator.glyph_size.x * 0.5,
- cursor_pos.y + decorator.glyph_size.y * 0.5,
- )
- radius = decorator.glyph_size.y * 0.35
- imgui.get_window_draw_list().add_circle_filled(center, radius, imgui.IM_COL32(255, 60, 60, 255))
- statics.editor.set_line_decorator(-2.0, decorator_callback)
- # Right-click on line numbers: toggle breakpoint
- def line_number_context_menu(data: TextEditor.PopupData):
- line = data.pos.line
- has = line in statics.breakpoints
- label = ("Remove breakpoint" if has else "Set breakpoint") + f" (line {line + 1})"
- if imgui.menu_item_simple(label):
- if has:
- statics.breakpoints.discard(line)
- else:
- statics.breakpoints.add(line)
- statics.editor.set_line_number_context_menu_callback(line_number_context_menu)
- # Right-click in the text: different context menu
- def text_context_menu(data: TextEditor.PopupData):
- line = data.pos.line
- column = data.pos.index
- if imgui.menu_item_simple("Go to definition"):
- statics.last_action = f"Go to definition at {line + 1}:{column + 1}"
- if imgui.menu_item_simple("Find references"):
- statics.last_action = f"Find references at {line + 1}:{column + 1}"
- statics.editor.set_text_context_menu_callback(text_context_menu)
- # Hover in text: live popup with the word under the mouse
- # (think IDE quick-info / type-on-hover).
- def text_hover(data: TextEditor.PopupData):
- word = statics.editor.get_word_at_mouse_pos(imgui.get_mouse_pos())
- imgui.text(f"Hovered: line {data.pos.line + 1}, col {data.pos.index + 1}")
- if word:
- imgui.text(f"Word: {word}")
- statics.text_hover = text_hover # kept so the toggle can re-install it
- statics.editor.set_text_hover_callback(text_hover)
- statics.initialized = True
- editor = statics.editor
- imgui.text(
- "Right-click line numbers (or F9) to toggle breakpoints. "
- "Right-click in text for a menu. Hover text for live info. "
- "Hover the colored gutter bands for marker tooltips."
- )
- if statics.last_action:
- imgui.text_colored(imgui.ImVec4(0.5, 0.8, 1.0, 1.0), statics.last_action)
- imgui.begin_disabled(not editor.has_markers())
- if imgui.small_button("Clear markers"):
- editor.clear_markers()
- imgui.end_disabled()
- imgui.same_line()
- mini_map = editor.is_show_mini_map_enabled()
- changed, mini_map = imgui.checkbox("Show Mini Map", mini_map)
- if changed:
- editor.set_show_mini_map_enabled(mini_map)
- imgui.same_line()
- hover_on = editor.has_text_hover_callback()
- changed, hover_on = imgui.checkbox("Hover hints", hover_on)
- if changed:
- if hover_on:
- editor.set_text_hover_callback(statics.text_hover)
- else:
- editor.clear_text_hover_callback()
- imgui.same_line()
- scrollbar_mini = editor.is_show_scrollbar_mini_map_enabled()
- changed, scrollbar_mini = imgui.checkbox("Show Scrollbar Mini Map", scrollbar_mini)
- if changed:
- editor.set_show_scrollbar_mini_map_enabled(scrollbar_mini)
- imgui.same_line()
- imgui.text_disabled("(ticks in the scrollbar at marker / selection lines)")
- imgui.new_line()
- imgui.new_line()
- code_font = imgui_md.get_code_font()
- imgui.push_font(code_font.font, code_font.size)
- editor.render("##decorators_ctx", ImVec2(-1, imgui.get_text_line_height() * 20))
- imgui.pop_font()
- # F9: toggle breakpoint on current line
- if imgui.shortcut(imgui.Key.f9):
- line = editor.get_main_cursor_position().line
- if line in statics.breakpoints:
- statics.breakpoints.discard(line)
- else:
- statics.breakpoints.add(line)
- # ============================================================================
- # Tab 5: Text Diff
- # Demonstrates: side-by-side text comparison with TextDiff
- # ============================================================================
- @static(initialized=False, diff=None, side_by_side=False)
- def demo_text_diff():
- _show_source_toggle(demo_text_diff)
- statics = demo_text_diff
- if not statics.initialized:
- # The long docstring line on the second row exists so that toggling
- # the Word Wrap checkbox produces a visible effect on the diff.
- left = (
- "import math\n"
- '"""Module that says hello. A tiny example used in the ImGui Bundle TextDiff demo to show how line-by-line comparison works."""\n'
- "\n"
- "def greet():\n"
- ' print("Hello")\n'
- " return 0\n"
- )
- right = (
- "import math\n"
- '"""Module that says hello to anyone by name. A tiny example used in the ImGui Bundle TextDiff demo to show how line-by-line comparison and word-wrap work together."""\n'
- "import os\n"
- "\n"
- "def greet(name):\n"
- ' print(f"Hello, {name}")\n'
- " return 0\n"
- )
- statics.diff = TextDiff()
- statics.diff.set_text(left, right)
- statics.diff.set_language(TextEditor.Language.python())
- statics.initialized = True
- changed, statics.side_by_side = imgui.checkbox("Side by side", statics.side_by_side)
- if changed:
- statics.diff.set_side_by_side_mode(statics.side_by_side)
- imgui.same_line()
- wrap = statics.diff.is_word_wrap_enabled()
- changed, wrap = imgui.checkbox("Word Wrap", wrap)
- if changed:
- statics.diff.set_word_wrap_enabled(wrap)
- code_font = imgui_md.get_code_font()
- imgui.push_font(code_font.font, code_font.size)
- statics.diff.render("##diff")
- imgui.pop_font()
- # ============================================================================
- # Tab 6: Editor with Menus
- # Demonstrates how menus can be added to supplement the editor
- # ============================================================================
- @static(initialized=False, editor=None, find_text="", replace_text="", case_sensitive=True, whole_word=False)
- def demo_editor_with_menus():
- _show_source_toggle(demo_editor_with_menus)
- statics = demo_editor_with_menus
- if not statics.initialized:
- statics.editor = TextEditor()
- statics.editor.set_text(
- "import math\n"
- "\n"
- "def greet(name):\n"
- ' print(f"Hello {name}")\n'
- " x = 42\n"
- " pi = math.pi\n"
- " return x\n"
- )
- statics.editor.set_language(TextEditor.Language.python())
- statics.initialized = True
- editor = statics.editor
- imgui.begin_child("editor_with_menus", ImVec2(0, 0), 0, imgui.WindowFlags_.menu_bar.value)
- import platform
- _shortcut = "Cmd-" if platform.system() == "Darwin" else "Ctrl-"
- if imgui.begin_menu_bar():
- if imgui.begin_menu("Edit"):
- if imgui.menu_item_simple("Undo", f"{_shortcut}Z", enabled=editor.can_undo()):
- editor.undo()
- if imgui.menu_item_simple("Redo", f"{_shortcut}Y", enabled=editor.can_redo()):
- editor.redo()
- imgui.separator()
- if imgui.menu_item_simple("Cut", f"{_shortcut}X", enabled=editor.any_cursor_has_selection()):
- editor.cut()
- if imgui.menu_item_simple("Copy", f"{_shortcut}C", enabled=editor.any_cursor_has_selection()):
- editor.copy()
- if imgui.menu_item_simple("Paste", f"{_shortcut}V"):
- editor.paste()
- imgui.separator()
- _, flag = imgui.menu_item("Insert Spaces on Tabs", "", editor.is_insert_spaces_on_tabs())
- if _:
- editor.set_insert_spaces_on_tabs(flag)
- if imgui.menu_item_simple("Tabs To Spaces"):
- editor.tabs_to_spaces()
- if imgui.menu_item_simple("Spaces To Tabs", enabled=not editor.is_insert_spaces_on_tabs()):
- editor.spaces_to_tabs()
- if imgui.menu_item_simple("Strip Trailing Whitespaces"):
- editor.strip_trailing_whitespaces()
- imgui.end_menu()
- if imgui.begin_menu("Selection"):
- if imgui.menu_item_simple("Select All", f"{_shortcut}A", enabled=not editor.is_empty()):
- editor.select_all()
- imgui.separator()
- if imgui.menu_item_simple("Indent Line(s)", f"{_shortcut}]", enabled=not editor.is_empty()):
- editor.indent_lines()
- if imgui.menu_item_simple("Deindent Line(s)", f"{_shortcut}[", enabled=not editor.is_empty()):
- editor.deindent_lines()
- if imgui.menu_item_simple("Move Line(s) Up", "Alt-Up", enabled=not editor.is_empty()):
- editor.move_up_lines()
- if imgui.menu_item_simple("Move Line(s) Down", "Alt-Down ", enabled=not editor.is_empty()):
- editor.move_down_lines()
- if imgui.menu_item_simple("Toggle Comments", f"{_shortcut}/", enabled=editor.has_language()):
- editor.toggle_comments()
- imgui.separator()
- if imgui.menu_item_simple("To Uppercase", enabled=editor.any_cursor_has_selection()):
- editor.selection_to_upper_case()
- if imgui.menu_item_simple("To Lowercase", enabled=editor.any_cursor_has_selection()):
- editor.selection_to_lower_case()
- imgui.separator()
- if imgui.menu_item_simple("Add Next Occurrence", f"{_shortcut}D", enabled=editor.current_cursor_has_selection()):
- editor.add_next_occurrence()
- if imgui.menu_item_simple("Select All Occurrences", f"^{_shortcut}D", enabled=editor.current_cursor_has_selection()):
- editor.select_all_occurrences()
- imgui.end_menu()
- if imgui.begin_menu("View"):
- _, flag = imgui.menu_item("Show Whitespaces", "", editor.is_show_whitespaces_enabled())
- if _:
- editor.set_show_whitespaces_enabled(flag)
- _, flag = imgui.menu_item("Show Spaces", "", editor.is_show_spaces_enabled())
- if _:
- editor.set_show_spaces_enabled(flag)
- _, flag = imgui.menu_item("Show Tabs", "", editor.is_show_tabs_enabled())
- if _:
- editor.set_show_tabs_enabled(flag)
- _, flag = imgui.menu_item("Show Line Numbers", "", editor.is_show_line_numbers_enabled())
- if _:
- editor.set_show_line_numbers_enabled(flag)
- _, flag = imgui.menu_item("Show Matching Brackets", "", editor.is_showing_matching_brackets())
- if _:
- editor.set_show_matching_brackets(flag)
- _, flag = imgui.menu_item("Complete Matching Glyphs", "", editor.is_completing_paired_glyphs())
- if _:
- editor.set_complete_paired_glyphs(flag)
- _, flag = imgui.menu_item("Show Pan/Scroll Indicator", "", editor.is_show_pan_scroll_indicator_enabled())
- if _:
- editor.set_show_pan_scroll_indicator_enabled(flag)
- _, flag = imgui.menu_item("Middle Mouse Pan Mode", "", editor.is_middle_mouse_pan_mode())
- if _:
- if flag:
- editor.set_middle_mouse_pan_mode()
- else:
- editor.set_middle_mouse_scroll_mode()
- imgui.separator()
- _, flag = imgui.menu_item("Word Wrap", "", editor.is_word_wrap_enabled())
- if _:
- editor.set_word_wrap_enabled(flag)
- _, flag = imgui.menu_item("Line Folding", "", editor.is_line_folding_enabled())
- if _:
- editor.set_line_folding_enabled(flag)
- _, flag = imgui.menu_item("Show Mini Map", "", editor.is_show_mini_map_enabled())
- if _:
- editor.set_show_mini_map_enabled(flag)
- _, flag = imgui.menu_item("Show Scrollbar Mini Map", "", editor.is_show_scrollbar_mini_map_enabled())
- if _:
- editor.set_show_scrollbar_mini_map_enabled(flag)
- imgui.end_menu()
- if imgui.begin_menu("Find"):
- if imgui.menu_item_simple("Find", f"{_shortcut}F"):
- editor.open_find_replace_window()
- if imgui.menu_item_simple("Find Next", f"{_shortcut}G", enabled=editor.has_find_string()):
- editor.find_next()
- # if imgui.menu_item_simple(f"Find All", f"Shift {_shortcut}G", enabled=editor.has_find_string()):
- # editor.find_all()
- imgui.end_menu()
- imgui.end_menu_bar()
- code_font = imgui_md.get_code_font()
- imgui.push_font(code_font.font, code_font.size)
- editor.render("##editor_menus")
- imgui.pop_font()
- imgui.end_child()
- # ============================================================================
- # Tab 7: Multi-Cursor
- # Demonstrates: multiple simultaneous cursors and selections.
- # get_number_of_cursors / get_cursor_selection / get_cursor_text.
- # ============================================================================
- @static(initialized=False, editor=None)
- def demo_multi_cursor():
- def _init_statics():
- statics = demo_multi_cursor
- if not statics.initialized:
- statics.editor = TextEditor()
- statics.editor.set_text(
- "# Multi-cursor playground.\n"
- "# - Alt-click (Mac) / Ctrl-click (Windows, Linux) to add a cursor\n"
- "# at the click location.\n"
- "# - Hold the same modifier and drag to add a cursor and extend its selection\n"
- "# - Double-click a word to select it, then Ctrl-D (or Command-D on Mac)\n"
- "# to add a cursor at the next occurrence of the same word.\n"
- "\n"
- "value = 1\n"
- "value = value + 1\n"
- "value = value * value\n"
- "value = value - 1\n"
- "print(value, value, value)\n"
- )
- statics.editor.set_language(TextEditor.Language.python())
- statics.initialized = True
- def _gui_select_occurrences():
- editor = demo_multi_cursor.editor
- # Quick-action buttons. add_next_occurrence / select_all_occurrences
- # require the current cursor to have a selection (a word to match).
- imgui.begin_disabled(not editor.current_cursor_has_selection())
- if imgui.small_button("Add Next Occurrence"):
- editor.add_next_occurrence()
- imgui.same_line()
- if imgui.small_button("Select All Occurrences"):
- editor.select_all_occurrences()
- imgui.end_disabled()
- imgui.same_line()
- imgui.begin_disabled(editor.get_number_of_cursors() <= 1)
- if imgui.small_button("Clear Extra Cursors"):
- editor.clear_cursors() # leaves a single cursor at the main location
- imgui.end_disabled()
- def _gui_editor():
- editor = demo_multi_cursor.editor
- code_font = imgui_md.get_code_font()
- imgui.push_font(code_font.font, code_font.size)
- editor_height = imgui.get_text_line_height() * 20
- editor.render("##multi_cursor", size=ImVec2(0, editor_height))
- imgui.pop_font()
- # Live cursor status panel
- def _gui_cursors_info():
- editor = demo_multi_cursor.editor
- n_cursors = editor.get_number_of_cursors()
- imgui.text(f"Cursors: {n_cursors}")
- for i in range(n_cursors):
- sel = editor.get_cursor_selection(i)
- text = editor.get_cursor_text(i)
- if not text:
- imgui.bullet_text(
- f"[{i}] L:{sel.start.line + 1} C:{sel.start.index + 1}"
- )
- else:
- imgui.bullet_text(
- f"[{i}] L:{sel.start.line + 1} C:{sel.start.index + 1} "
- f"-> L:{sel.end.line + 1} C:{sel.end.index + 1} "
- f"selected: {text!r}"
- )
- # Main gui for demo_multi_cursors
- _init_statics()
- _show_source_toggle(demo_multi_cursor)
- _gui_select_occurrences()
- _gui_editor()
- _gui_cursors_info()
- # ============================================================================
- # Main demo function
- # ============================================================================
- def demo_gui():
- imgui_md.render(
- """
- # ImGuiColorTextEdit
- [ImGuiColorTextEdit](https://github.com/goossens/ImGuiColorTextEdit) is a syntax highlighting text editor for Dear ImGui (originally by BalazsJako, rewritten by Johan A. Goossens)
- """
- )
- if imgui.begin_tab_bar("##TextEditorDemos"):
- if imgui.begin_tab_item("Basic Editor")[0]:
- demo_basic_editor()
- imgui.end_tab_item()
- if imgui.begin_tab_item("Change Callback")[0]:
- demo_change_callback()
- imgui.end_tab_item()
- if imgui.begin_tab_item("Annotations & Interactivity")[0]:
- demo_decorators_and_context_menus()
- imgui.end_tab_item()
- if imgui.begin_tab_item("Filters")[0]:
- demo_filters()
- imgui.end_tab_item()
- if imgui.begin_tab_item("Text Diff")[0]:
- demo_text_diff()
- imgui.end_tab_item()
- if imgui.begin_tab_item("Multi-Cursor")[0]:
- demo_multi_cursor()
- imgui.end_tab_item()
- if imgui.begin_tab_item("Editor with Menus")[0]:
- demo_editor_with_menus()
- imgui.end_tab_item()
- imgui.end_tab_bar()
- def main():
- from imgui_bundle import immapp
- immapp.run(demo_gui, with_markdown=True, window_size=(1000, 800))
- if __name__ == "__main__":
- main()
|