Implement the basic shortcuts for window management

This commit is contained in:
Kovid Goyal 2016-12-03 12:48:37 +05:30
parent cb4454c562
commit 836494a8f0
5 changed files with 92 additions and 26 deletions

View File

@ -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())

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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()