Merge pull request #6 from adafruit/move_editor

Moving editor into apps and more
This commit is contained in:
foamyguy 2025-05-05 20:54:09 -05:00 committed by GitHub
commit b1930c7231
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 764 additions and 44 deletions

View file

@ -12,7 +12,7 @@ LEARN_PROJECT_URLS = [
"https://cdn-learn.adafruit.com/downloads/zip/3194658/Metro/Metro_RP2350_FlappyNyanCat.zip?timestamp={}",
"https://cdn-learn.adafruit.com/downloads/zip/3196927/Metro/Metro_RP2350_Match3/match3_game.zip?timestamp={}",
"https://cdn-learn.adafruit.com/downloads/zip/3194422/Metro/Metro_RP2350_Breakout.zip?timestamp={}",
"https://cdn-learn.adafruit.com/downloads/zip/3196755/Metro/Metro_RP2350_Chips_Challenge.zip?timestamp=1746112286",
"https://cdn-learn.adafruit.com/downloads/zip/3196755/Metro/Metro_RP2350_Chips_Challenge.zip?timestamp={}",
]
def create_font_specific_zip(font_path: Path, src_dir: Path, learn_projects_dir: Path, output_dir: Path):
@ -99,6 +99,9 @@ def create_font_specific_zip(font_path: Path, src_dir: Path, learn_projects_dir:
# Write the file
with open(target, 'wb') as f:
f.write(source.read())
# copy builtin apps
shutil.copytree("builtin_apps", apps_dir, dirs_exist_ok=True)
# Create the final zip file
with zipfile.ZipFile(output_zip, 'w', zipfile.ZIP_DEFLATED) as zf:

View file

@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2024 Tim Cocks
#
# SPDX-License-Identifier: MIT

View file

@ -0,0 +1,148 @@
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import select
import sys
# pylint: disable=no-self-use
try:
import termios
_orig_attr = None # pylint: disable=invalid-name
def _nonblocking():
global _orig_attr # pylint: disable=global-statement
_orig_attr = termios.tcgetattr(sys.stdin)
attr = termios.tcgetattr(sys.stdin)
attr[3] &= ~(termios.ECHO | termios.ICANON)
attr[6][termios.VMIN] = 1
attr[6][termios.VTIME] = 0
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, attr)
def _blocking():
if _orig_attr is not None:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, _orig_attr)
except ImportError:
def _nonblocking():
pass
def _blocking():
pass
# LINES = 24
# COLS = 80
LINES = 33
COLS = 120
special_keys = {
"\x1b": ..., # all prefixes of special keys must be entered as Ellipsis
"\x1b[": ...,
"\x1b[3": ...,
"\x1b[5": ...,
"\x1b[6": ...,
"\x1b[A": "KEY_UP",
"\x1b[B": "KEY_DOWN",
"\x1b[C": "KEY_RIGHT",
"\x1b[D": "KEY_LEFT",
"\x1b[H": "KEY_HOME",
"\x1b[F": "KEY_END",
"\x1b[5~": "KEY_PGUP",
"\x1b[6~": "KEY_PGDN",
"\x1b[3~": "KEY_DELETE",
}
class Screen:
def __init__(self, terminal=None):
self._poll = select.poll()
self._poll.register(sys.stdin, select.POLLIN)
self._pending = ""
self._terminal = terminal
def _sys_stdin_readable(self):
return hasattr(sys.stdin, "readable") and sys.stdin.readable()
def _sys_stdout_flush(self):
if hasattr(sys.stdout, "flush"):
sys.stdout.flush()
def _terminal_read_blocking(self):
return sys.stdin.read(1)
def _terminal_read_timeout(self, timeout):
if self._sys_stdin_readable() or self._poll.poll(timeout):
r = sys.stdin.read(1)
return r
return None
def move(self, y, x):
if self._terminal is not None:
self._terminal.write(f"\033[{y+1};{x+1}H")
else:
print(end=f"\033[{y+1};{x+1}H")
def erase(self):
if self._terminal is not None:
self._terminal.write("\033H\033[2J")
else:
print(end="\033H\033[2J")
def addstr(self, y, x, text):
self.move(y, x)
if self._terminal is not None:
self._terminal.write(text)
else:
print(end=text)
def getkey(self):
self._sys_stdout_flush()
pending = self._pending
if pending and (code := special_keys.get(pending)) is None:
self._pending = pending[1:]
return pending[0]
while True:
if pending:
c = self._terminal_read_timeout(50)
if c is None:
self._pending = pending[1:]
return pending[0]
else:
c = self._terminal_read_blocking()
c = pending + c
code = special_keys.get(c)
if code is None:
self._pending = c[1:]
return c[0]
if code is not Ellipsis:
return code
pending = c
def wrapper(func, *args, **kwds):
stdscr = Screen()
try:
_nonblocking()
return func(stdscr, *args, **kwds)
finally:
_blocking()
stdscr.move(LINES - 1, 0)
print("\n")
def custom_terminal_wrapper(terminal, func, *args, **kwds):
stdscr = Screen(terminal)
try:
_nonblocking()
return func(stdscr, *args, **kwds)
finally:
_blocking()
stdscr.move(LINES - 1, 0)
print("\n")

View file

@ -0,0 +1,390 @@
# SPDX-FileCopyrightText: 2020 Wasim Lorgat
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import gc
import os
import microcontroller
import supervisor
import usb_cdc
from . import dang as curses
from . import util
from adafruit_pathlib import Path
import time
import json
from argv_file_helper import argv_filename
# pylint: disable=redefined-builtin
# def print(message):
# usb_cdc.data.write(f"{message}\r\n".encode("utf-8"))
INPUT_DISPLAY_REFRESH_COOLDOWN = 0.3 # s
SHOW_MEMFREE = False
class MaybeDisableReload:
def __enter__(self):
try:
from supervisor import runtime # pylint: disable=import-outside-toplevel
except ImportError:
return
self._old_autoreload = ( # pylint: disable=attribute-defined-outside-init
runtime.autoreload
)
runtime.autoreload = False
def __exit__(self, exc_type, exc_value, traceback):
try:
from supervisor import runtime # pylint: disable=import-outside-toplevel
except ImportError:
return
runtime.autoreload = self._old_autoreload
def os_exists(filename):
try:
os.stat(filename)
return True
except OSError:
return False
def gc_mem_free_hint():
if not SHOW_MEMFREE:
return ""
if hasattr(gc, "mem_free"):
gc.collect()
return f" | free: {gc.mem_free()}"
return ""
class Buffer:
def __init__(self, lines):
self.lines = lines
def __len__(self):
return len(self.lines)
def __getitem__(self, index):
return self.lines[index]
@property
def bottom(self):
return len(self) - 1
def insert(self, cursor, string):
row, col = cursor.row, cursor.col
# print(f"len: {len(self.lines)}")
# print(f"row: {row}")
try:
current = self.lines.pop(row)
except IndexError:
current = ""
new = current[:col] + string + current[col:]
self.lines.insert(row, new)
def split(self, cursor):
row, col = cursor.row, cursor.col
current = self.lines.pop(row)
self.lines.insert(row, current[:col])
self.lines.insert(row + 1, current[col:])
def delete(self, cursor):
row, col = cursor.row, cursor.col
if (row, col) < (self.bottom, len(self[row])):
current = self.lines.pop(row)
if col < len(current):
new = current[:col] + current[col + 1:]
self.lines.insert(row, new)
else:
nextline = self.lines.pop(row)
new = current + nextline
self.lines.insert(row, new)
def clamp(x, lower, upper):
if x < lower:
return lower
if x > upper:
return upper
return x
class Cursor:
def __init__(self, row=0, col=0, col_hint=None):
self.row = row
self._col = col
self._col_hint = col if col_hint is None else col_hint
@property
def col(self):
return self._col
@col.setter
def col(self, col):
self._col = col
self._col_hint = col
def _clamp_col(self, buffer):
self._col = min(self._col_hint, len(buffer[self.row]))
def up(self, buffer): # pylint: disable=invalid-name
if self.row > 0:
self.row -= 1
self._clamp_col(buffer)
# print(f"cursor pos: {self.row}, {self.col}")
def down(self, buffer):
if self.row < len(buffer) - 1:
self.row += 1
self._clamp_col(buffer)
# print(f"cursor pos: {self.row}, {self.col}")
def left(self, buffer):
if self.col > 0:
self.col -= 1
# print(f"cursor pos: {self.row}, {self.col}")
elif self.row > 0:
self.row -= 1
self.col = len(buffer[self.row])
# print(f"cursor pos: {self.row}, {self.col}")
def right(self, buffer):
# print(f"len: {len(buffer)}")
if len(buffer) > 0 and self.col < len(buffer[self.row]):
self.col += 1
# print(f"cursor pos: {self.row}, {self.col}")
elif self.row < len(buffer) - 1:
self.row += 1
self.col = 0
# print(f"cursor pos: {self.row}, {self.col}")
def end(self, buffer):
self.col = len(buffer[self.row])
# print(f"cursor pos: {self.row}, {self.col}")
class Window:
def __init__(self, n_rows, n_cols, row=0, col=0):
self.n_rows = n_rows
self.n_cols = n_cols
self.row = row
self.col = col
@property
def bottom(self):
return self.row + self.n_rows - 1
def up(self, cursor): # pylint: disable=invalid-name
if cursor.row == self.row - 1 and self.row > 0:
self.row -= 1
def down(self, buffer, cursor):
if cursor.row == self.bottom + 1 and self.bottom < len(buffer) - 1:
self.row += 1
def horizontal_scroll(self, cursor, left_margin=5, right_margin=2):
n_pages = cursor.col // (self.n_cols - right_margin)
self.col = max(n_pages * self.n_cols - right_margin - left_margin, 0)
def translate(self, cursor):
return cursor.row - self.row, cursor.col - self.col
def left(window, buffer, cursor):
cursor.left(buffer)
window.up(cursor)
window.horizontal_scroll(cursor)
def right(window, buffer, cursor):
cursor.right(buffer)
window.down(buffer, cursor)
window.horizontal_scroll(cursor)
def home(window, buffer, cursor): # pylint: disable=unused-argument
cursor.col = 0
window.horizontal_scroll(cursor)
def end(window, buffer, cursor):
cursor.end(buffer)
window.horizontal_scroll(cursor)
def editor(stdscr, filename, visible_cursor): # pylint: disable=too-many-branches,too-many-statements
if os_exists(filename):
with open(filename, "r", encoding="utf-8") as f:
buffer = Buffer(f.read().splitlines())
else:
buffer = Buffer([""])
user_message = None
window = Window(curses.LINES - 1, curses.COLS - 1)
cursor = Cursor()
visible_cursor.text = buffer[0][0]
last_refresh_time = -1
stdscr.erase()
img = [None] * curses.LINES
def setline(row, line):
if img[row] == line:
return
img[row] = line
line += " " * (window.n_cols - len(line))
stdscr.addstr(row, 0, line)
while True:
lastrow = 0
for row, line in enumerate(buffer[window.row: window.row + window.n_rows]):
lastrow = row
if row == cursor.row - window.row and window.col > 0:
line = "«" + line[window.col + 1:]
if len(line) > window.n_cols:
line = line[: window.n_cols - 1] + "»"
setline(row, line)
for row in range(lastrow + 1, window.n_rows):
setline(row, "~~ EOF ~~")
row = curses.LINES - 1
if user_message is None:
if util.readonly():
line = f"{filename:12} (mnt RO ^W) | ^R run | ^C: quit{gc_mem_free_hint()}"
else:
line = f"{filename:12} (mnt RW ^W) | ^R run | ^S save | ^X: save & exit | ^C: exit no save{gc_mem_free_hint()}"
else:
line = user_message
user_message = None
setline(row, line)
stdscr.move(*window.translate(cursor))
old_cursor_pos = (cursor.col, cursor.row)
# display.refresh(minimum_frames_per_second=20)
k = stdscr.getkey()
# print(repr(k))
if len(k) == 1 and " " <= k <= "~":
buffer.insert(cursor, k)
for _ in k:
right(window, buffer, cursor)
elif k == "\x18": # ctrl-x
if not util.readonly():
with open(filename, "w", encoding="utf-8") as f:
for row in buffer:
f.write(f"{row}\n")
return
else:
print("Unable to Save due to readonly mode! File Contents:")
print("---- begin file contents ----")
for row in buffer:
print(row)
print("---- end file contents ----")
elif k == "\x13": # Ctrl-S
if not util.readonly():
with open(filename, "w", encoding="utf-8") as f:
for row in buffer:
f.write(f"{row}\n")
user_message = "Saved"
else:
print("Unable to Save due to readonly mode!")
elif k == "\x11": # Ctrl-Q
print("ctrl-Q")
for row in buffer:
print(row)
elif k == "\x17": # Ctrl-W
boot_args_file = argv_filename("/boot.py")
with open(boot_args_file, "w") as f:
f.write(json.dumps([not util.readonly(), "/apps/editor/code.py", Path(filename).absolute()]))
microcontroller.reset()
elif k == "\x12": # Ctrl-R
print(f"Run: {filename}")
launcher_code_args_file = argv_filename("/code.py")
with open(launcher_code_args_file, "w") as f:
f.write(json.dumps(["/apps/editor/code.py", Path(filename).absolute()]))
supervisor.set_next_code_file(filename, sticky_on_reload=False, reload_on_error=True,
working_directory=Path(filename).parent.absolute())
supervisor.reload()
elif k == "KEY_HOME":
home(window, buffer, cursor)
elif k == "KEY_END":
end(window, buffer, cursor)
elif k == "KEY_LEFT":
left(window, buffer, cursor)
elif k == "KEY_DOWN":
cursor.down(buffer)
window.down(buffer, cursor)
window.horizontal_scroll(cursor)
print(f"scroll pos: {window.row}")
elif k == "KEY_PGDN":
for _ in range(window.n_rows):
cursor.down(buffer)
window.down(buffer, cursor)
window.horizontal_scroll(cursor)
elif k == "KEY_UP":
cursor.up(buffer)
window.up(cursor)
window.horizontal_scroll(cursor)
elif k == "KEY_PGUP":
for _ in range(window.n_rows):
cursor.up(buffer)
window.up(cursor)
window.horizontal_scroll(cursor)
elif k == "KEY_RIGHT":
right(window, buffer, cursor)
elif k == "\n":
buffer.split(cursor)
right(window, buffer, cursor)
elif k in ("KEY_DELETE", "\x04"):
print("delete")
if cursor.row < len(buffer.lines) - 1 or \
cursor.col < len(buffer.lines[cursor.row]):
buffer.delete(cursor)
try:
visible_cursor.text = buffer.lines[cursor.row][cursor.col]
except IndexError:
visible_cursor.text = " "
elif k in ("KEY_BACKSPACE", "\x7f", "\x08"):
print(f"backspace {bytes(k, 'utf-8')}")
if (cursor.row, cursor.col) > (0, 0):
left(window, buffer, cursor)
buffer.delete(cursor)
else:
print(f"unhandled k: {k}")
print(f"unhandled K: {ord(k)}")
print(f"unhandled k: {bytes(k, 'utf-8')}")
# print("updating visible cursor")
# print(f"anchored pos: {((cursor.col * 6) - 1, (cursor.row * 12) + 20)}")
if old_cursor_pos != (cursor.col, cursor.row):
# terminal_tilegrid.pixel_shader[old_cursor_pos[0], old_cursor_pos[1]] = [0,1]
# terminal_tilegrid.pixel_shader[cursor.col, cursor.row] = [1,0]
# visible_cursor.anchored_position = ((cursor.col * 6) - 1, (cursor.row * 12) + 20)
visible_cursor.anchored_position = ((cursor.col * 6), ((cursor.row - window.row) * 12))
try:
visible_cursor.text = buffer.lines[cursor.row][cursor.col]
except IndexError:
visible_cursor.text = " "
def edit(filename, terminal=None, visible_cursor=None):
with MaybeDisableReload():
if terminal is None:
return curses.wrapper(editor, filename)
else:
return curses.custom_terminal_wrapper(terminal, editor, filename, visible_cursor)

View file

@ -0,0 +1,158 @@
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import os
import usb_cdc
from . import dang as curses
from . import util
# pylint: disable=redefined-builtin
# def print(message):
# usb_cdc.data.write(f"{message}\r\n".encode("utf-8"))
always = ["code.py", "boot.py", "settings.toml", "boot_out.txt"]
good_extensions = [".py", ".toml", ".txt", ".json"]
def os_exists(filename):
try:
os.stat(filename)
return True
except OSError:
return False
def isdir(filename):
return os.stat(filename)[0] & 0o40_000
def has_good_extension(filename):
for g in good_extensions:
if filename.endswith(g):
return True
return False
def picker(stdscr, options, notes=(), start_idx=0):
stdscr.erase()
visible_files = None
if len(options) > curses.LINES - 1:
visible_files = options[:curses.LINES - 1]
else:
visible_files = options
scroll_offset = 0
need_to_scroll = False
# del options[curses.LINES - 1:]
print(f"len opts: {len(options)}")
print(f"len vis: {len(visible_files)}")
def _draw_file_list():
for row, option in enumerate(visible_files):
if row < len(notes) and (note := notes[row]):
option = f"{option} {note}"
try:
space_count = max(len(visible_files[row + 1]), len(visible_files[row - 1])) - len(option)
if space_count < 0:
space_count = 0
except IndexError:
space_count = curses.COLS - len(option)
stdscr.addstr(row, 3, option + " " * space_count)
stdscr.addstr(curses.LINES - 1, 0, "Enter: select | ^C: quit | ^N: New")
_draw_file_list()
old_idx = None
idx = start_idx
while True:
if need_to_scroll:
need_to_scroll = False
_draw_file_list()
if idx != old_idx:
if old_idx is not None:
stdscr.addstr(old_idx, 0, " ")
stdscr.addstr(idx, 0, "=>")
old_idx = idx
k = stdscr.getkey()
if k == "KEY_DOWN":
print(f"{scroll_offset + len(visible_files)} < {len(options)}")
if scroll_offset + len(visible_files) < len(options):
if idx == len(visible_files) - 1:
need_to_scroll = True
scroll_offset += 1
visible_files = options[scroll_offset:scroll_offset + curses.LINES - 1]
idx = min(idx + 1, len(visible_files) - 1)
elif k == "KEY_UP":
if scroll_offset > 0:
if idx == 0:
need_to_scroll = True
scroll_offset -= 1
visible_files = options[scroll_offset:scroll_offset + curses.LINES - 1]
idx = max(idx - 1, 0)
elif k == "\n":
if visible_files[idx] == "../":
os.chdir("../")
options, notes = _files_list()
return picker(stdscr, options, notes)
elif isdir(visible_files[idx]):
os.chdir(visible_files[idx])
options, notes = _files_list()
return picker(stdscr, options, notes)
else:
return visible_files[idx]
# ctrl-N
elif k == "\x0E":
if not util.readonly():
new_file_name = new_file(stdscr)
if new_file_name is not None:
return new_file_name
# pylint: disable=inconsistent-return-statements
def new_file(stdscr):
stdscr.erase()
new_file_name = input("New File Name: ")
if os_exists(new_file_name):
print("Error: File Already Exists")
return
with open(new_file_name, "w") as f:
f.write("")
return new_file_name
def _files_list():
options = sorted(
(
g
for g in os.listdir(".")
if not g.startswith(".")
),
key=lambda filename: (not has_good_extension(filename), filename),
) # + always[:]
if os.getcwd() != "/":
options.insert(0, "../")
# notes = [None if os_exists(filename) else "(NEW)" for filename in options]
notes = [None] * len(options)
return options, notes
def pick_file(terminal):
os.chdir("/")
options, notes = _files_list()
return curses.custom_terminal_wrapper(terminal, picker, options, notes)

View file

@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT
def readonly():
try:
import storage # pylint: disable=import-outside-toplevel
except ImportError:
return False
return storage.getmount("/").readonly

View file

@ -9,7 +9,7 @@ from adafruit_bitmap_font import bitmap_font
from adafruit_editor import editor, picker
from tilepalettemapper import TilePaletteMapper
import json
from argv_file_helper import argv_filename
from adafruit_argv_file import read_argv, write_argv
from adafruit_fruitjam.peripherals import request_display_config
request_display_config(720, 400)
@ -44,19 +44,11 @@ visible_cursor.anchor_point = (0, 0)
visible_cursor.anchored_position = (0, 0)
main_group.append(visible_cursor)
file = None
try:
editor_argv_file = argv_filename(__file__)
with open(editor_argv_file, "r") as f:
argv_data = json.load(f)
file = argv_data[0]
os.remove(editor_argv_file)
except OSError:
file = "boot_out.txt"
args = read_argv(__file__)
if args is not None and len(args) > 0:
file = args[0]
else:
file = picker.pick_file(terminal)
print(f"opening {file}")
editor.edit(file, terminal, visible_cursor)
print("after edit")
# while True:
# pass

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,4 @@
{
"title": "Editor",
"icon": "icon.bmp"
}

View file

@ -15,18 +15,21 @@ import displayio
import supervisor
import sys
import terminalio
import usb
import adafruit_pathlib as pathlib
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.text_box import TextBox
from adafruit_display_text.bitmap_label import Label
from adafruit_displayio_layout.layouts.grid_layout import GridLayout
from adafruit_anchored_tilegrid import AnchoredTileGrid
import adafruit_imageload
import adafruit_usb_host_descriptors
from adafruit_anchored_group import AnchoredGroup
from adafruit_fruitjam.peripherals import request_display_config
from argv_file_helper import argv_filename
from adafruit_argv_file import read_argv, write_argv
"""
desktop launcher code.py arguments
@ -35,34 +38,24 @@ desktop launcher code.py arguments
1-N: args to pass to next code file
"""
try:
arg_file = argv_filename(__file__)
print(f"arg files: {arg_file}")
with open(arg_file, "r") as f:
args = json.load(f)
os.remove(arg_file)
args = read_argv(__file__)
if args is not None and len(args) > 0:
next_code_file = None
remaining_args = None
if len(args) > 0:
next_code_file = args[0]
if len(args) > 1:
remaining_args = args[1:]
if remaining_args is not None:
next_code_argv_filename = argv_filename(next_code_file)
with open(next_code_argv_filename, "w") as f:
f.write(json.dumps(remaining_args))
write_argv(next_code_file, remaining_args)
next_code_file = next_code_file
supervisor.set_next_code_file(next_code_file)
supervisor.set_next_code_file(next_code_file, sticky_on_reload=False, reload_on_error=True,
working_directory="/".join(next_code_file.split("/")[:-1]))
print(f"launching: {next_code_file}")
supervisor.reload()
# os.rename("/saves/.boot_py_argv", "/saves/.not_boot_py_argv")
except OSError:
# no args, just launch desktop
pass
request_display_config(720, 400)
display = supervisor.runtime.display
@ -73,14 +66,18 @@ if display.width > 360:
font_file = "/fonts/terminal.lvfontbin"
font = bitmap_font.load_font(font_file)
main_group = displayio.Group(scale=scale)
scaled_group = displayio.Group(scale=scale)
main_group = displayio.Group()
main_group.append(scaled_group)
display.root_group = main_group
background_bmp = displayio.Bitmap(display.width, display.height, 1)
bg_palette = displayio.Palette(1)
bg_palette[0] = 0x222222
bg_tg = displayio.TileGrid(bitmap=background_bmp, pixel_shader=bg_palette)
main_group.append(bg_tg)
scaled_group.append(bg_tg)
# load the mouse cursor bitmap
mouse_bmp = displayio.OnDiskBitmap("launcher_assets/mouse_cursor.bmp")
@ -209,12 +206,12 @@ default_icon_bmp, default_icon_palette = adafruit_imageload.load("launcher_asset
default_icon_palette.make_transparent(0)
menu_grid = GridLayout(x=40, y=16, width=WIDTH, height=HEIGHT, grid_size=(config["width"], config["height"]),
divider_lines=False)
main_group.append(menu_grid)
scaled_group.append(menu_grid)
menu_title_txt = Label(font, text="Fruit Jam OS")
menu_title_txt.anchor_point = (0.5, 0.5)
menu_title_txt.anchored_position = (display.width // (2 * scale), 2)
main_group.append(menu_title_txt)
scaled_group.append(menu_title_txt)
app_titles = []
apps = []
@ -258,7 +255,10 @@ for path in app_path.iterdir():
"icon": str(icon_file.absolute()) if icon_file is not None else None,
"file": str(code_file.absolute())
})
i += 1
def reuse_cell(grid_coords):
try:
cell_group = menu_grid.get_content(grid_coords)
@ -364,15 +364,26 @@ right_tg.anchor_point = (1.0, 0.5)
right_tg.anchored_position = ((display.width // scale) - 4, (display.height // 2 // scale) - 2)
original_arrow_btn_color = left_palette[2]
main_group.append(left_tg)
main_group.append(right_tg)
scaled_group.append(left_tg)
scaled_group.append(right_tg)
if len(apps) <= 6:
right_tg.hidden = True
left_tg.hidden = True
if mouse:
main_group.append(mouse_tg)
scaled_group.append(mouse_tg)
help_txt = Label(terminalio.FONT, text="[Arrow]: Move\n[E]: Edit\n[Enter]: Run")
# help_txt = TextBox(terminalio.FONT, width=88, height=30, align=TextBox.ALIGN_RIGHT, background_color=0x008800, text="[E]: Edit\n[Enter]: Run")
help_txt.anchor_point = (0, 0)
help_txt.anchored_position = (2, 2)
# help_txt.anchored_position = (display.width - 89, 1)
print(help_txt.bounding_box)
main_group.append(help_txt)
def atexit_callback():
@ -540,10 +551,11 @@ while True:
print(f"editor selected: {apps[editor_index]}")
edit_file = apps[editor_index]["file"]
launch_file = "/code_editor.py"
with open(argv_filename(launch_file), "w") as f:
f.write(json.dumps([apps[editor_index]["file"]]))
editor_launch_file = "apps/editor/code.py"
write_argv(editor_launch_file, [apps[editor_index]["file"]])
# with open(argv_filename(launch_file), "w") as f:
# f.write(json.dumps([apps[editor_index]["file"]]))
supervisor.set_next_code_file(launch_file, sticky_on_reload=False, reload_on_error=True,
working_directory="/".join(launch_file.split("/")[:-1]))
supervisor.set_next_code_file(editor_launch_file, sticky_on_reload=False, reload_on_error=True,
working_directory="/".join(editor_launch_file.split("/")[:-1]))
supervisor.reload()