Implement the basic shortcuts for window management
This commit is contained in:
parent
cb4454c562
commit
836494a8f0
@ -21,6 +21,11 @@ Cursor = namedtuple('Cursor', 'x y hidden shape color blink')
|
||||
if DATA_CELL_SIZE % 3:
|
||||
raise ValueError('Incorrect data cell size, must be a multiple of 3')
|
||||
|
||||
|
||||
def color_as_int(val):
|
||||
return val[0] << 16 | val[1] << 8 | val[2]
|
||||
|
||||
|
||||
# cell shader {{{
|
||||
|
||||
cell_shader = (
|
||||
@ -145,10 +150,6 @@ void main() {
|
||||
# }}}
|
||||
|
||||
|
||||
def color_as_int(val):
|
||||
return val[0] << 16 | val[1] << 8 | val[2]
|
||||
|
||||
|
||||
class Selection: # {{{
|
||||
|
||||
__slots__ = tuple('in_progress start_x start_y start_scrolled_by end_x end_y end_scrolled_by'.split())
|
||||
|
||||
@ -110,12 +110,21 @@ color15 #ffffff
|
||||
# Key mapping
|
||||
# For a list of key names, see: http://www.glfw.org/docs/latest/group__keys.html
|
||||
# For a list of modifier names, see: http://www.glfw.org/docs/latest/group__mods.html
|
||||
|
||||
# Clipboard
|
||||
map ctrl+shift+v paste_from_clipboard
|
||||
map ctrl+shift+s paste_from_selection
|
||||
map ctrl+shift+c copy_to_clipboard
|
||||
|
||||
# Scrolling
|
||||
map ctrl+shift+up scroll_line_up
|
||||
map ctrl+shift+down scroll_line_down
|
||||
map ctrl+shift+page_up scroll_page_up
|
||||
map ctrl+shift+page_down scroll_page_down
|
||||
map ctrl+shift+home scroll_home
|
||||
map ctrl+shift+end scroll_end
|
||||
|
||||
# Window management
|
||||
map ctrl+shift+n new_window
|
||||
map ctrl+shift+tab next_window
|
||||
map ctrl+shift+w close_window
|
||||
|
||||
@ -35,13 +35,21 @@ class Layout:
|
||||
self.opts = opts
|
||||
self.border_width = border_width
|
||||
|
||||
def add_window(self, windows, window):
|
||||
def next_window(self, windows, active_window_idx):
|
||||
active_window_idx = (active_window_idx + 1) % len(windows)
|
||||
self.set_active_window(windows, active_window_idx)
|
||||
return active_window_idx
|
||||
|
||||
def add_window(self, windows, window, active_window_idx):
|
||||
raise NotImplementedError()
|
||||
|
||||
def remove_window(self, windows, window):
|
||||
def remove_window(self, windows, window, active_window_idx):
|
||||
raise NotImplementedError()
|
||||
|
||||
def __call__(self, windows):
|
||||
def set_active_window(self, windows, active_window_idx):
|
||||
raise NotImplementedError()
|
||||
|
||||
def __call__(self, windows, active_window_idx):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@ -50,18 +58,26 @@ class Stack(Layout):
|
||||
name = 'stack'
|
||||
needs_window_borders = False
|
||||
|
||||
def add_window(self, windows, window):
|
||||
windows.appendleft(window)
|
||||
self(windows)
|
||||
def add_window(self, windows, window, active_window_idx):
|
||||
windows.append(window)
|
||||
active_window_idx = len(windows) - 1
|
||||
self(windows, active_window_idx)
|
||||
return active_window_idx
|
||||
|
||||
def remove_window(self, windows, window):
|
||||
def remove_window(self, windows, window, active_window_idx):
|
||||
windows.remove(window)
|
||||
self(windows)
|
||||
active_window_idx = max(0, min(active_window_idx, len(windows) - 1))
|
||||
self(windows, active_window_idx)
|
||||
return active_window_idx
|
||||
|
||||
def __call__(self, windows):
|
||||
def set_active_window(self, windows, active_window_idx):
|
||||
for i, w in enumerate(windows):
|
||||
w.is_visible_in_layout = i == active_window_idx
|
||||
|
||||
def __call__(self, windows, active_window_idx):
|
||||
xstart, xnum = next(layout_dimension(viewport_size.width, cell_size.width))
|
||||
ystart, ynum = next(layout_dimension(available_height(), cell_size.height))
|
||||
wg = WindowGeometry(left=xstart, top=ystart, xnum=xnum, ynum=ynum, right=xstart + cell_size.width * xnum, bottom=ystart + cell_size.height * ynum)
|
||||
for i, w in enumerate(windows):
|
||||
w.is_visible_in_layout = i == 0
|
||||
w.is_visible_in_layout = i == active_window_idx
|
||||
w.set_geometry(wg)
|
||||
|
||||
@ -67,6 +67,7 @@ class Tab:
|
||||
def __init__(self, opts, args):
|
||||
self.opts, self.args = opts, args
|
||||
self.windows = deque()
|
||||
self.active_window_idx = 0
|
||||
self.borders = Borders(opts)
|
||||
self.current_layout = Stack(opts, self.borders.border_width)
|
||||
|
||||
@ -76,7 +77,7 @@ class Tab:
|
||||
|
||||
@property
|
||||
def active_window(self):
|
||||
return self.windows[0] if self.windows else None
|
||||
return self.windows[self.active_window_idx] if self.windows else None
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
@ -89,7 +90,7 @@ class Tab:
|
||||
|
||||
def relayout(self):
|
||||
if self.windows:
|
||||
self.current_layout(self.windows)
|
||||
self.current_layout(self.windows, self.active_window_idx)
|
||||
self.borders(self.windows, self.active_window, self.current_layout.needs_window_borders)
|
||||
|
||||
def launch_child(self, use_shell=False):
|
||||
@ -101,14 +102,36 @@ class Tab:
|
||||
ans.fork()
|
||||
return ans
|
||||
|
||||
def new_window(self, use_shell=False):
|
||||
def new_window(self, use_shell=True):
|
||||
child = self.launch_child(use_shell=use_shell)
|
||||
window = Window(self, child, self.opts, self.args)
|
||||
tab_manager().add_child_fd(child.child_fd, window.read_ready, window.write_ready)
|
||||
self.current_layout.add_window(self.windows, window)
|
||||
self.active_window_idx = self.current_layout.add_window(self.windows, window, self.active_window_idx)
|
||||
glfw_post_empty_event()
|
||||
|
||||
def close_window(self):
|
||||
if self.windows:
|
||||
self.remove_window(self.windows[self.active_window_idx])
|
||||
glfw_post_empty_event()
|
||||
|
||||
def remove_window(self, window):
|
||||
self.current_layout.remove_window(self.windows, window)
|
||||
self.active_window_idx = self.current_layout.remove_window(self.windows, window, self.active_window_idx)
|
||||
glfw_post_empty_event()
|
||||
|
||||
def set_active_window(self, window):
|
||||
try:
|
||||
idx = self.windows.index(window)
|
||||
except ValueError:
|
||||
return
|
||||
if idx != self.active_window_idx:
|
||||
self.current_layout.set_active_window(self.windows, idx)
|
||||
self.active_window_idx = idx
|
||||
glfw_post_empty_event()
|
||||
|
||||
def next_window(self):
|
||||
if len(self.windows) > 1:
|
||||
self.active_window_idx = self.current_layout.next_window(self.windows, self.active_window_idx)
|
||||
glfw_post_empty_event()
|
||||
|
||||
def __iter__(self):
|
||||
yield from iter(self.windows)
|
||||
@ -227,8 +250,10 @@ class TabManager(Thread):
|
||||
self.pending_ui_thread_calls.put((func, args))
|
||||
glfw_post_empty_event()
|
||||
|
||||
def close_window(self, window):
|
||||
def close_window(self, window=None):
|
||||
' Can be called in either thread, will first kill the child (with SIGHUP), then remove the window from the gui '
|
||||
if window is None:
|
||||
window = self.active_window
|
||||
if current_thread() is main_thread:
|
||||
self.queue_action(self.close_window, window)
|
||||
else:
|
||||
@ -307,8 +332,10 @@ class TabManager(Thread):
|
||||
if action == GLFW_PRESS or action == GLFW_REPEAT:
|
||||
func = get_shortcut(self.opts.keymap, mods, key)
|
||||
tab = self.active_tab
|
||||
if tab is None:
|
||||
return
|
||||
if func is not None:
|
||||
f = getattr(self, func, getattr(tab, func, None))
|
||||
f = getattr(self, func, None)
|
||||
if f is not None:
|
||||
passthrough = f()
|
||||
if not passthrough:
|
||||
@ -317,7 +344,7 @@ class TabManager(Thread):
|
||||
if window is not None:
|
||||
yield window
|
||||
if func is not None:
|
||||
f = getattr(window, func, None)
|
||||
f = getattr(tab, func, getattr(window, func, None))
|
||||
if f is not None:
|
||||
passthrough = f()
|
||||
if not passthrough:
|
||||
@ -352,10 +379,11 @@ class TabManager(Thread):
|
||||
return
|
||||
focus_moved = False
|
||||
old_focus = self.active_window
|
||||
if button == GLFW_MOUSE_BUTTON_1 and w is not old_focus:
|
||||
# TODO: Switch focus to this window
|
||||
focus_moved = True
|
||||
tab = self.active_tab
|
||||
yield
|
||||
if button == GLFW_MOUSE_BUTTON_1 and w is not old_focus:
|
||||
tab.set_active_window(w)
|
||||
focus_moved = True
|
||||
if focus_moved:
|
||||
if old_focus is not None and not old_focus.destroyed:
|
||||
old_focus.focus_changed(False)
|
||||
|
||||
@ -33,7 +33,7 @@ class Window:
|
||||
self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)
|
||||
self.needs_layout = True
|
||||
self.title = appname
|
||||
self.is_visible_in_layout = True
|
||||
self._is_visible_in_layout = True
|
||||
self.child, self.opts = child, opts
|
||||
self.child_fd = child.child_fd
|
||||
self.screen = Screen(self, 24, 80, opts.scrollback_lines)
|
||||
@ -42,6 +42,18 @@ class Window:
|
||||
self.write_buf = memoryview(b'')
|
||||
self.char_grid = CharGrid(self.screen, opts)
|
||||
|
||||
@property
|
||||
def is_visible_in_layout(self):
|
||||
return self._is_visible_in_layout
|
||||
|
||||
@is_visible_in_layout.setter
|
||||
def is_visible_in_layout(self, val):
|
||||
val = bool(val)
|
||||
if val != self._is_visible_in_layout:
|
||||
self._is_visible_in_layout = val
|
||||
if val:
|
||||
self.refresh()
|
||||
|
||||
def refresh(self):
|
||||
self.screen.mark_as_dirty()
|
||||
wakeup()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user