class_docstr: A base class for sidebars using Text.
code: def bind_events(self):
      self.text["yscrollcommand"] = self.redirect_yscroll_event
  
      # Ensure focus is always redirected to the main editor text widget.
      self.main_widget.bind("<FocusIn>", self.redirect_focusin_event)
  
      # Redirect mouse scrolling to the main editor text widget.
      #
      # Note that without this, scrolling with the mouse only scrolls
      # the line numbers.
      self.main_widget.bind(
          "<MouseWheel>", self.redirect_mousewheel_event
      )
  
      # Redirect mouse button events to the main editor text widget,
      # except for the left mouse button (1).
      #
      # Note: X-11 sends Button-4 and Button-5 events for the scroll wheel.
      def bind_mouse_event(event_name, target_event_name):
          handler = functools.partial(
              self.redirect_mousebutton_event,
              event_name=target_event_name,
          )
          self.main_widget.bind(event_name, handler)
  
      for button in [2, 3, 4, 5]:
          for event_name in (
              f"<Button-{button}>",
              f"<ButtonRelease-{button}>",
              f"<B{button}-Motion>",
          ):
              bind_mouse_event(event_name, target_event_name=event_name)
  
          # Convert double- and triple-click events to normal click events,
          # since event_generate() doesn't allow generating such events.
          for event_name in (
              f"<Double-Button-{button}>",
              f"<Triple-Button-{button}>",
          ):
              bind_mouse_event(
                  event_name, target_event_name=f"<Button-{button}>"
              )
  
      # start_line is set upon <Button-1> to allow selecting a range of rows
      # by dragging.  It is cleared upon <ButtonRelease-1>.
      start_line = None
  
      # last_y is initially set upon <B1-Leave> and is continuously updated
      # upon <B1-Motion>, until <B1-Enter> or the mouse button is released.
      # It is used in text_auto_scroll(), which is called repeatedly and
      # does have a mouse event available.
      last_y = None
  
      # auto_scrolling_after_id is set whenever text_auto_scroll is
      # scheduled via .after().  It is used to stop the auto-scrolling
      # upon <B1-Enter>, as well as to avoid scheduling the function several
      # times in parallel.
      auto_scrolling_after_id = None
  
      def drag_update_selection_and_insert_mark(y_coord):
          """Helper function for drag and selection event handlers."""
          lineno = get_lineno(self.text, f"@0,{y_coord}")
          a, b = sorted([start_line, lineno])
          self.text.tag_remove("sel", "1.0", "end")
          self.text.tag_add("sel", f"{a}.0", f"{b+1}.0")
          self.text.mark_set(
              "insert", f"{lineno if lineno == a else lineno + 1}.0"
          )
  
      def b1_mousedown_handler(event):
          nonlocal start_line
          nonlocal last_y
          start_line = int(float(self.text.index(f"@0,{event.y}")))
          last_y = event.y
  
          drag_update_selection_and_insert_mark(event.y)
  
      self.main_widget.bind("<Button-1>", b1_mousedown_handler)
  
      def b1_mouseup_handler(event):
          # On mouse up, we're no longer dragging.  Set the shared persistent
          # variables to None to represent this.
          nonlocal start_line
          nonlocal last_y
          start_line = None
          last_y = None
          self.text.event_generate("<ButtonRelease-1>", x=0, y=event.y)
  
      self.main_widget.bind("<ButtonRelease-1>", b1_mouseup_handler)
  
      def b1_drag_handler(event):
          nonlocal last_y
          if last_y is None:  # i.e. if not currently dragging
              return
          last_y = event.y
          drag_update_selection_and_insert_mark(event.y)
  
      self.main_widget.bind("<B1-Motion>", b1_drag_handler)
  
      def text_auto_scroll():
          """Mimic Text auto-scrolling when dragging outside of it."""
          # See: https://github.com/tcltk/tk/blob/064ff9941b4b80b85916a8afe86a6c21fd388b54/library/text.tcl#L670
          nonlocal auto_scrolling_after_id
          y = last_y
          if y is None:
              self.main_widget.after_cancel(auto_scrolling_after_id)
              auto_scrolling_after_id = None
              return
          elif y < 0:
              self.text.yview_scroll(-1 + y, "pixels")
              drag_update_selection_and_insert_mark(y)
          elif y > self.main_widget.winfo_height():
              self.text.yview_scroll(
                  1 + y - self.main_widget.winfo_height(), "pixels"
              )
              drag_update_selection_and_insert_mark(y)
          auto_scrolling_after_id = self.main_widget.after(
              50, text_auto_scroll
          )
  
      def b1_leave_handler(event):
          # Schedule the initial call to text_auto_scroll(), if not already
          # scheduled.
          nonlocal auto_scrolling_after_id
          if auto_scrolling_after_id is None:
              nonlocal last_y
              last_y = event.y
              auto_scrolling_after_id = self.main_widget.after(
                  0, text_auto_scroll
              )
  
      self.main_widget.bind("<B1-Leave>", b1_leave_handler)
  
      def b1_enter_handler(event):
          # Cancel the scheduling of text_auto_scroll(), if it exists.
          nonlocal auto_scrolling_after_id
          if auto_scrolling_after_id is not None:
              self.main_widget.after_cancel(auto_scrolling_after_id)
              auto_scrolling_after_id = None
  
      self.main_widget.bind("<B1-Enter>", b1_enter_handler)
  
imports: import contextlib
  import functools
  import itertools
  import tkinter
  from tkinter.font import Font
  from idlelib.config import idleConf
  from idlelib.delegator import Delegator
  from idlelib import macosx
label_desc: Use image node_id 13 for calling the BaseSideBar obj's underlying member method code with example usage: obj.bind_events() without return types