1#!/usr/local/bin/python3.8
2# vim:fileencoding=utf-8
3# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
4
5from enum import IntFlag
6from itertools import chain
7from typing import Sequence, Tuple
8
9from .fast_data_types import (
10    BORDERS_PROGRAM, add_borders_rect, compile_program, get_options,
11    init_borders_program, os_window_has_background_image
12)
13from .typing import LayoutType
14from .utils import load_shaders
15from .window_list import WindowGroup, WindowList
16
17
18class BorderColor(IntFlag):
19    # See the border vertex shader for how these flags become actual colors
20    default_bg, active, inactive, window_bg, bell = ((1 << i) for i in range(5))
21
22
23def vertical_edge(os_window_id: int, tab_id: int, color: int, width: int, top: int, bottom: int, left: int) -> None:
24    if width > 0:
25        add_borders_rect(os_window_id, tab_id, left, top, left + width, bottom, color)
26
27
28def horizontal_edge(os_window_id: int, tab_id: int, color: int, height: int, left: int, right: int, top: int) -> None:
29    if height > 0:
30        add_borders_rect(os_window_id, tab_id, left, top, right, top + height, color)
31
32
33def draw_edges(os_window_id: int, tab_id: int, colors: Sequence[int], wg: WindowGroup, borders: bool = False) -> None:
34    geometry = wg.geometry
35    if geometry is None:
36        return
37    pl, pt = wg.effective_padding('left'), wg.effective_padding('top')
38    pr, pb = wg.effective_padding('right'), wg.effective_padding('bottom')
39    left = geometry.left - pl
40    top = geometry.top - pt
41    lr = geometry.right
42    right = lr + pr
43    bt = geometry.bottom
44    bottom = bt + pb
45    if borders:
46        width = wg.effective_border()
47        bt = bottom
48        lr = right
49        left -= width
50        top -= width
51        right += width
52        bottom += width
53        pl = pr = pb = pt = width
54    horizontal_edge(os_window_id, tab_id, colors[1], pt, left, right, top)
55    horizontal_edge(os_window_id, tab_id, colors[3], pb, left, right, bt)
56    vertical_edge(os_window_id, tab_id, colors[0], pl, top, bottom, left)
57    vertical_edge(os_window_id, tab_id, colors[2], pr, top, bottom, lr)
58
59
60def load_borders_program() -> None:
61    compile_program(BORDERS_PROGRAM, *load_shaders('border'))
62    init_borders_program()
63
64
65class Borders:
66
67    def __init__(self, os_window_id: int, tab_id: int):
68        self.os_window_id = os_window_id
69        self.tab_id = tab_id
70
71    def __call__(
72        self,
73        all_windows: WindowList,
74        current_layout: LayoutType,
75        extra_blank_rects: Sequence[Tuple[int, int, int, int]],
76        draw_window_borders: bool = True,
77    ) -> None:
78        opts = get_options()
79        draw_active_borders = opts.active_border_color is not None
80        draw_minimal_borders = opts.draw_minimal_borders and max(opts.window_margin_width) < 1
81        add_borders_rect(self.os_window_id, self.tab_id, 0, 0, 0, 0, BorderColor.default_bg)
82        has_background_image = os_window_has_background_image(self.os_window_id)
83        if not has_background_image:
84            for br in chain(current_layout.blank_rects, extra_blank_rects):
85                left, top, right, bottom = br
86                add_borders_rect(self.os_window_id, self.tab_id, left, top, right, bottom, BorderColor.default_bg)
87        bw = 0
88        groups = tuple(all_windows.iter_all_layoutable_groups(only_visible=True))
89        if groups:
90            bw = groups[0].effective_border()
91        draw_borders = bw > 0 and draw_window_borders
92        active_group = all_windows.active_group
93
94        for i, wg in enumerate(groups):
95            window_bg = wg.default_bg
96            window_bg = (window_bg << 8) | BorderColor.window_bg
97            if draw_borders and not draw_minimal_borders:
98                # Draw the border rectangles
99                if wg is active_group and draw_active_borders:
100                    color = BorderColor.active
101                else:
102                    color = BorderColor.bell if wg.needs_attention else BorderColor.inactive
103                draw_edges(self.os_window_id, self.tab_id, (color, color, color, color), wg, borders=True)
104            if not has_background_image:
105                # Draw the background rectangles over the padding region
106                colors = window_bg, window_bg, window_bg, window_bg
107                draw_edges(self.os_window_id, self.tab_id, colors, wg)
108
109        if draw_minimal_borders:
110            for border_line in current_layout.get_minimal_borders(all_windows):
111                left, top, right, bottom = border_line.edges
112                add_borders_rect(self.os_window_id, self.tab_id, left, top, right, bottom, border_line.color)
113