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:
|
if DATA_CELL_SIZE % 3:
|
||||||
raise ValueError('Incorrect data cell size, must be a multiple of 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 {{{
|
||||||
|
|
||||||
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: # {{{
|
class Selection: # {{{
|
||||||
|
|
||||||
__slots__ = tuple('in_progress start_x start_y start_scrolled_by end_x end_y end_scrolled_by'.split())
|
__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
|
# Key mapping
|
||||||
# For a list of key names, see: http://www.glfw.org/docs/latest/group__keys.html
|
# 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
|
# 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+v paste_from_clipboard
|
||||||
map ctrl+shift+s paste_from_selection
|
map ctrl+shift+s paste_from_selection
|
||||||
map ctrl+shift+c copy_to_clipboard
|
map ctrl+shift+c copy_to_clipboard
|
||||||
|
|
||||||
|
# Scrolling
|
||||||
map ctrl+shift+up scroll_line_up
|
map ctrl+shift+up scroll_line_up
|
||||||
map ctrl+shift+down scroll_line_down
|
map ctrl+shift+down scroll_line_down
|
||||||
map ctrl+shift+page_up scroll_page_up
|
map ctrl+shift+page_up scroll_page_up
|
||||||
map ctrl+shift+page_down scroll_page_down
|
map ctrl+shift+page_down scroll_page_down
|
||||||
map ctrl+shift+home scroll_home
|
map ctrl+shift+home scroll_home
|
||||||
map ctrl+shift+end scroll_end
|
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.opts = opts
|
||||||
self.border_width = border_width
|
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()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def remove_window(self, windows, window):
|
def remove_window(self, windows, window, active_window_idx):
|
||||||
raise NotImplementedError()
|
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()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
@ -50,18 +58,26 @@ class Stack(Layout):
|
|||||||
name = 'stack'
|
name = 'stack'
|
||||||
needs_window_borders = False
|
needs_window_borders = False
|
||||||
|
|
||||||
def add_window(self, windows, window):
|
def add_window(self, windows, window, active_window_idx):
|
||||||
windows.appendleft(window)
|
windows.append(window)
|
||||||
self(windows)
|
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)
|
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))
|
xstart, xnum = next(layout_dimension(viewport_size.width, cell_size.width))
|
||||||
ystart, ynum = next(layout_dimension(available_height(), cell_size.height))
|
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)
|
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):
|
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)
|
w.set_geometry(wg)
|
||||||
|
|||||||
@ -67,6 +67,7 @@ class Tab:
|
|||||||
def __init__(self, opts, args):
|
def __init__(self, opts, args):
|
||||||
self.opts, self.args = opts, args
|
self.opts, self.args = opts, args
|
||||||
self.windows = deque()
|
self.windows = deque()
|
||||||
|
self.active_window_idx = 0
|
||||||
self.borders = Borders(opts)
|
self.borders = Borders(opts)
|
||||||
self.current_layout = Stack(opts, self.borders.border_width)
|
self.current_layout = Stack(opts, self.borders.border_width)
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ class Tab:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def active_window(self):
|
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
|
@property
|
||||||
def title(self):
|
def title(self):
|
||||||
@ -89,7 +90,7 @@ class Tab:
|
|||||||
|
|
||||||
def relayout(self):
|
def relayout(self):
|
||||||
if self.windows:
|
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)
|
self.borders(self.windows, self.active_window, self.current_layout.needs_window_borders)
|
||||||
|
|
||||||
def launch_child(self, use_shell=False):
|
def launch_child(self, use_shell=False):
|
||||||
@ -101,14 +102,36 @@ class Tab:
|
|||||||
ans.fork()
|
ans.fork()
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def new_window(self, use_shell=False):
|
def new_window(self, use_shell=True):
|
||||||
child = self.launch_child(use_shell=use_shell)
|
child = self.launch_child(use_shell=use_shell)
|
||||||
window = Window(self, child, self.opts, self.args)
|
window = Window(self, child, self.opts, self.args)
|
||||||
tab_manager().add_child_fd(child.child_fd, window.read_ready, window.write_ready)
|
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):
|
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):
|
def __iter__(self):
|
||||||
yield from iter(self.windows)
|
yield from iter(self.windows)
|
||||||
@ -227,8 +250,10 @@ class TabManager(Thread):
|
|||||||
self.pending_ui_thread_calls.put((func, args))
|
self.pending_ui_thread_calls.put((func, args))
|
||||||
glfw_post_empty_event()
|
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 '
|
' 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:
|
if current_thread() is main_thread:
|
||||||
self.queue_action(self.close_window, window)
|
self.queue_action(self.close_window, window)
|
||||||
else:
|
else:
|
||||||
@ -307,8 +332,10 @@ class TabManager(Thread):
|
|||||||
if action == GLFW_PRESS or action == GLFW_REPEAT:
|
if action == GLFW_PRESS or action == GLFW_REPEAT:
|
||||||
func = get_shortcut(self.opts.keymap, mods, key)
|
func = get_shortcut(self.opts.keymap, mods, key)
|
||||||
tab = self.active_tab
|
tab = self.active_tab
|
||||||
|
if tab is None:
|
||||||
|
return
|
||||||
if func is not None:
|
if func is not None:
|
||||||
f = getattr(self, func, getattr(tab, func, None))
|
f = getattr(self, func, None)
|
||||||
if f is not None:
|
if f is not None:
|
||||||
passthrough = f()
|
passthrough = f()
|
||||||
if not passthrough:
|
if not passthrough:
|
||||||
@ -317,7 +344,7 @@ class TabManager(Thread):
|
|||||||
if window is not None:
|
if window is not None:
|
||||||
yield window
|
yield window
|
||||||
if func is not None:
|
if func is not None:
|
||||||
f = getattr(window, func, None)
|
f = getattr(tab, func, getattr(window, func, None))
|
||||||
if f is not None:
|
if f is not None:
|
||||||
passthrough = f()
|
passthrough = f()
|
||||||
if not passthrough:
|
if not passthrough:
|
||||||
@ -352,10 +379,11 @@ class TabManager(Thread):
|
|||||||
return
|
return
|
||||||
focus_moved = False
|
focus_moved = False
|
||||||
old_focus = self.active_window
|
old_focus = self.active_window
|
||||||
if button == GLFW_MOUSE_BUTTON_1 and w is not old_focus:
|
tab = self.active_tab
|
||||||
# TODO: Switch focus to this window
|
|
||||||
focus_moved = True
|
|
||||||
yield
|
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 focus_moved:
|
||||||
if old_focus is not None and not old_focus.destroyed:
|
if old_focus is not None and not old_focus.destroyed:
|
||||||
old_focus.focus_changed(False)
|
old_focus.focus_changed(False)
|
||||||
|
|||||||
@ -33,7 +33,7 @@ class Window:
|
|||||||
self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)
|
self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)
|
||||||
self.needs_layout = True
|
self.needs_layout = True
|
||||||
self.title = appname
|
self.title = appname
|
||||||
self.is_visible_in_layout = True
|
self._is_visible_in_layout = True
|
||||||
self.child, self.opts = child, opts
|
self.child, self.opts = child, opts
|
||||||
self.child_fd = child.child_fd
|
self.child_fd = child.child_fd
|
||||||
self.screen = Screen(self, 24, 80, opts.scrollback_lines)
|
self.screen = Screen(self, 24, 80, opts.scrollback_lines)
|
||||||
@ -42,6 +42,18 @@ class Window:
|
|||||||
self.write_buf = memoryview(b'')
|
self.write_buf = memoryview(b'')
|
||||||
self.char_grid = CharGrid(self.screen, opts)
|
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):
|
def refresh(self):
|
||||||
self.screen.mark_as_dirty()
|
self.screen.mark_as_dirty()
|
||||||
wakeup()
|
wakeup()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user