OSDN Git Service

eba355d39ef9d06cc0d05db0af401b6fec7372e7
[joypy/Thun.git] / joy / gui / main.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 ('''\
4 Joypy - Copyright © 2018 Simon Forman
5 '''
6 'This program comes with ABSOLUTELY NO WARRANTY; for details right-click "warranty".'
7 ' This is free software, and you are welcome to redistribute it under certain conditions;'
8 ' right-click "sharing" for details.'
9 ' Right-click on these commands to see docs on UI commands: key_bindings mouse_bindings')
10 import os, pickle, sys
11 from textwrap import dedent
12
13 from joy.gui.textwidget import TextViewerWidget, tk, get_font, TEXT_BINDINGS
14 from joy.gui.utils import init_home, FileFaker
15 from joy.gui.world import StackDisplayWorld
16 from joy.library import initialize
17 from joy.utils.stack import stack_to_string
18
19
20 tb = TEXT_BINDINGS.copy()
21 tb.update({
22   '<F3>': lambda tv: tv.copy_selection_to_stack,
23   '<F4>': lambda tv: tv.cut,
24   #  '<F-->': lambda tv: tv.pastecut,
25   #  '<F6>': lambda tv: tv.copyto,
26   })
27 defaults = dict(text_bindings=tb, width=80, height=25)
28
29
30 GLOBAL_COMMANDS = {
31   '<F5>': 'swap',
32   '<F6>': 'dup',
33
34   '<Shift-F5>': 'roll<',
35   '<Shift-F6>': 'roll>',
36
37   '<F7>': 'over',
38   '<Shift-F7>': 'tuck',
39
40   '<Shift-F3>': 'parse',
41
42   '<F12>': 'words',
43   '<F1>': 'reset_log show_log',
44   '<Escape>': 'clear reset_log show_log',
45   '<Control-Delete>': 'pop',
46   '<Control-Shift-Delete>': 'popd',
47   }
48
49
50 def repo_relative_path(path):
51   return os.path.relpath(
52     path,
53     os.path.commonprefix((repo.controldir(), path))
54     )
55
56
57 def key_bindings(*args):
58   print dedent('''
59     Ctrl-Enter - Run the selection as Joy code.
60
61     F1 - Reset and show (if hidden) the log.
62     Esc - Like F1 but also clears the stack.
63     ...
64     F12 - print a list of all command words, or right-click "words".
65     ''')
66   return args
67
68
69 def mouse_bindings(*args):
70   print dedent('''
71     Mouse button chords (to cancel a chord, click the third mouse button.)
72
73     Left - Point, sweep selection
74     Left-Middle - Copy the selection, place text on stack
75     Left-Right - Run the selection as Joy code
76
77     Middle - Paste selection (bypass stack); click and drag to scroll.
78     Middle-Left - Paste from top of stack, preserve
79     Middle-Right - Paste from top of stack, pop
80
81     Right - Execute command word under mouse cursor
82     Right-Left - Print docs of command word under mouse cursor
83     Right-Middle - Lookup word (kinda useless now)
84     ''')
85   return args
86
87
88 def reset_log(*args):
89   log.delete('0.0', tk.END)
90   print __doc__
91   return args
92
93
94 def show_log(*args):
95   log_window.wm_deiconify()
96   log_window.update()
97   return args
98
99
100 def grand_reset(s, e, d):
101   stack = world.load_stack() or ()
102   log.reset()
103   t.reset()
104   return stack, e, d
105
106
107 JOY_HOME, repo = init_home()
108 STACK_FN = os.path.join(JOY_HOME, 'stack.pickle')
109 REL_STACK_FN = repo_relative_path(STACK_FN)
110 JOY_FN = os.path.join(JOY_HOME, 'scratch.txt')
111 LOG_FN = os.path.join(JOY_HOME, 'log.txt')
112 D = initialize()
113 for func in (
114   reset_log,
115   show_log,
116   grand_reset,
117   key_bindings,
118   mouse_bindings,
119   ):
120   D[func.__name__] = func
121 world = StackDisplayWorld(repo, STACK_FN, REL_STACK_FN, dictionary=D)
122 t = TextViewerWidget(world, **defaults)
123 log_window = tk.Toplevel()
124 log_window.protocol("WM_DELETE_WINDOW", log_window.withdraw)
125 log = TextViewerWidget(world, log_window, **defaults)
126 FONT = get_font('Iosevka', size=14)  # Requires Tk root already set up.
127 log.init('Log', LOG_FN, repo_relative_path(LOG_FN), repo, FONT)
128 t.init('Joy - ' + JOY_HOME, JOY_FN, repo_relative_path(JOY_FN), repo, FONT)
129 for event, command in GLOBAL_COMMANDS.items():
130   t.bind_all(event, lambda _, _command=command: world.interpret(_command))
131
132
133 def main():
134   sys.stdout, old_stdout = FileFaker(log), sys.stdout
135   try:
136     t.mainloop()
137   finally:
138     sys.stdout = old_stdout
139   return 0
140
141
142 if __name__ == '__main__':
143   main()