Esempio n. 1
0
    def __init__(self, decorator):
        super(CalculatorWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="calculator", doublebuffer=True)
        self.move(100,100)
        self.decorator = decorator

        def add_string(button):
            self.add_string(button.text)

        def clear(button):
            self.clear_text()

        def calculate(button):
            self.calculate()

        self.buttons = [
            [Button("C",clear),      None,                   Button("(",add_string), Button(")",add_string)],
            [Button("7",add_string), Button("8",add_string), Button("9",add_string), Button("/",add_string)],
            [Button("4",add_string), Button("5",add_string), Button("6",add_string), Button("*",add_string)],
            [Button("1",add_string), Button("2",add_string), Button("3",add_string), Button("-",add_string)],
            [Button("0",add_string), Button(".",add_string), Button("=",calculate),  Button("+",add_string)],
        ]

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)
        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/calculator.png",_description,"calculator")
        def help_browser(action):
            subprocess.Popen(["help-browser.py","calculator.trt"])
        menus = [
            ("File", [
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.tr = text_region.TextRegion(self.decorator.left_width(self)+5,self.decorator.top_height(self)+self.menubar.height,self.base_width-10,40)
        self.tr.set_font(toaru_fonts.Font(toaru_fonts.FONT_MONOSPACE,18))
        self.tr.set_text("")
        self.tr.set_alignment(1)
        self.tr.set_valignment(2)
        self.tr.set_one_line()
        self.tr.set_ellipsis()

        self.error = False

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None
Esempio n. 2
0
    def __init__(self, decorator, path):
        super(PDFViewerWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="pdfviewer", doublebuffer=True)
        self.move(100,100)
        self.x = 100
        self.y = 100
        self.decorator = decorator

        self.fitz_lib = ctypes.CDLL('libtoaru-fitz.so')
        self.fitz_lib.init_fitz.restype = ctypes.c_void_p
        self.fitz_lib.load_document.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
        self.fitz_lib.load_document.restype = ctypes.c_void_p
        self.fitz_lib.set_fit.argtypes = [ctypes.c_int]
        self.fitz_lib.draw_page.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p]
        self.fitz_lib.page_count.argtypes = [ctypes.c_void_p]
        self.fitz_lib.page_count.restype = ctypes.c_int

        def open_file(action):
            OpenFileDialog(self.decorator,"Open PDF...",glob="*.pdf",callback=self.load_file,window=self)

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)
        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/external/pdfviewer.png",_description,"pdfviewer")
        def help_browser(action):
            subprocess.Popen(["help-browser.py","downloaded/mupdf.trt"])
        menus = [
            ("File", [
                MenuEntryAction("Open","open",open_file,None),
                MenuEntryDivider(),
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None

        self.buf = None
        self.load_file(path)
        self.hilighted = None
Esempio n. 3
0
    def __init__(self, decorator):
        super(PackageManagerWindow,
              self).__init__(self.base_width + decorator.width(),
                             self.base_height + decorator.height(),
                             title="Package Manager",
                             icon="package",
                             doublebuffer=True)
        self.move(100, 100)
        self.x = 100
        self.y = 100
        self.decorator = decorator

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)

        def about_window(action):
            AboutAppletWindow(self.decorator, f"About Package Manager",
                              "/usr/share/icons/48/package.png", _description,
                              "package")

        def help_browser(action):
            subprocess.Popen(["help-browser.py", "packages.trt"])

        menus = [
            ("File", [
                MenuEntryAction("Exit", "exit", exit_app, None),
            ]),
            ("Help", [
                MenuEntryAction("Contents", "help", help_browser, None),
                MenuEntryDivider(),
                MenuEntryAction("About Package Manager", "star", about_window,
                                None),
            ]),
        ]

        self.menubar = MenuBarWidget(self, menus)

        self.menus = {}
        self.hovered_menu = None

        self.scroll_y = 0
        self.hilighted = None
        self.buf = None
        self.hilighted = None
Esempio n. 4
0
    def __init__(self, decorator):
        super(CalculatorWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="calculator", doublebuffer=True)
        self.move(100,100)
        self.decorator = decorator

        def add_string(button):
            self.add_string(button.text)

        def clear(button):
            self.clear_text()

        def calculate(button):
            self.calculate()

        self.buttons = [
            [Button("C",clear),      None,                   Button("(",add_string), Button(")",add_string)],
            [Button("7",add_string), Button("8",add_string), Button("9",add_string), Button("/",add_string)],
            [Button("4",add_string), Button("5",add_string), Button("6",add_string), Button("*",add_string)],
            [Button("1",add_string), Button("2",add_string), Button("3",add_string), Button("-",add_string)],
            [Button("0",add_string), Button(".",add_string), Button("=",calculate),  Button("+",add_string)],
        ]

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)
        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/calculator.png",_description,"calculator")
        def help_browser(action):
            subprocess.Popen(["help-browser.py","calculator.trt"])
        menus = [
            ("File", [
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.tr = text_region.TextRegion(self.decorator.left_width()+5,self.decorator.top_height()+self.menubar.height,self.base_width-10,40)
        self.tr.set_font(toaru_fonts.Font(toaru_fonts.FONT_MONOSPACE,18))
        self.tr.set_text("")
        self.tr.set_alignment(1)
        self.tr.set_valignment(2)
        self.tr.set_one_line()
        self.tr.set_ellipsis()

        self.error = False

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None
Esempio n. 5
0
    def __init__(self, decorator, path):
        super(FileBrowserWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="folder", doublebuffer=True)
        self.move(100,100)
        self.x = 100
        self.y = 100
        self.decorator = decorator

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)
        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/folder.png",_description,"folder")
        def help_browser(action):
            subprocess.Popen(["help-browser.py","file_browser.trt"])

        def input_path(action):
            def input_callback(input_window):
                text = input_window.tr.text
                input_window.close()
                self.load_directory(text)
            TextInputWindow(self.decorator,"Open directory...","open",text=self.path,callback=input_callback,window=self)

        menus = [
            ("File", [
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Go", [
                MenuEntryAction("Path...","open",input_path,None),
                MenuEntryDivider(),
                MenuEntryAction("Home","home",self.load_directory,os.environ.get("HOME")),
                MenuEntryAction("File System",None,self.load_directory,"/"),
                MenuEntryAction("Up","up",self.go_up,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None

        self.buf = None
        self.load_directory(path)
        self.hilighted = None
Esempio n. 6
0
    def __init__(self, decorator):
        super(PackageManagerWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="package", doublebuffer=True)
        self.move(100,100)
        self.x = 100
        self.y = 100
        self.decorator = decorator

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)
        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/package.png",_description,"package")
        def help_browser(action):
            subprocess.Popen(["help-browser.py","packages.trt"])
        def refresh_index(action):
            global _manifest
            msk.fetch_manifest()
            _manifest = msk.get_manifest()
            self.redraw_buf()
            self.draw()
        menus = [
            ("File", [
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Index", [
                MenuEntryAction("Refresh","refresh",refresh_index,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.menus = {}
        self.hovered_menu = None

        self.scroll_y = 0
        self.hilighted = None
        self.buf = None
        self.hilighted = None
Esempio n. 7
0
class CalculatorWindow(yutani.Window):

    base_width = 200
    base_height = 240

    def __init__(self, decorator):
        super(CalculatorWindow,
              self).__init__(self.base_width + decorator.width(),
                             self.base_height + decorator.height(),
                             title=app_name,
                             icon="calculator",
                             doublebuffer=True)
        self.move(100, 100)
        self.decorator = decorator

        def add_string(button):
            self.add_string(button.text)

        def clear(button):
            self.clear_text()

        def calculate(button):
            self.calculate()

        self.buttons = [
            [
                Button("C", clear), None,
                Button("(", add_string),
                Button(")", add_string)
            ],
            [
                Button("7", add_string),
                Button("8", add_string),
                Button("9", add_string),
                Button("/", add_string)
            ],
            [
                Button("4", add_string),
                Button("5", add_string),
                Button("6", add_string),
                Button("*", add_string)
            ],
            [
                Button("1", add_string),
                Button("2", add_string),
                Button("3", add_string),
                Button("-", add_string)
            ],
            [
                Button("0", add_string),
                Button(".", add_string),
                Button("=", calculate),
                Button("+", add_string)
            ],
        ]

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)

        def about_window(action):
            AboutAppletWindow(self.decorator, f"About {app_name}",
                              "/usr/share/icons/48/calculator.png",
                              _description, "calculator")

        def help_browser(action):
            subprocess.Popen(["help-browser.py", "calculator.trt"])

        menus = [
            ("File", [
                MenuEntryAction("Exit", "exit", exit_app, None),
            ]),
            ("Help", [
                MenuEntryAction("Contents", "help", help_browser, None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}", "star", about_window,
                                None),
            ]),
        ]

        self.menubar = MenuBarWidget(self, menus)

        self.tr = text_region.TextRegion(
            self.decorator.left_width() + 5,
            self.decorator.top_height() + self.menubar.height,
            self.base_width - 10, 40)
        self.tr.set_font(toaru_fonts.Font(toaru_fonts.FONT_MONOSPACE, 18))
        self.tr.set_text("")
        self.tr.set_alignment(1)
        self.tr.set_valignment(2)
        self.tr.set_one_line()
        self.tr.set_ellipsis()

        self.error = False

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None

    def calculate(self):
        if self.error or len(self.tr.text) == 0:
            self.tr.set_text("0")
            self.error = False
        try:
            self.tr.set_text(str(eval_expr(self.tr.text)))
        except Exception as e:
            error = str(e)
            if "(" in error:
                error = error[:error.find("(") - 1]
            self.tr.set_richtext(
                f"<i><color 0xFF0000>{e.__class__.__name__}</color>: {error}</i>"
            )
            self.error = True
        self.draw()
        self.flip()

    def add_string(self, text):
        if self.error:
            self.tr.text = ""
            self.error = False
        self.tr.set_text(self.tr.text + text)
        self.draw()
        self.flip()

    def clear_text(self):
        self.error = False
        self.tr.set_text("")
        self.draw()
        self.flip()

    def clear_last(self):
        if self.error:
            self.error = False
            self.tr.set_text("")
        if len(self.tr.text):
            self.tr.set_text(self.tr.text[:-1])
        self.draw()
        self.flip()

    def draw(self):
        surface = self.get_cairo_surface()

        WIDTH, HEIGHT = self.width - self.decorator.width(
        ), self.height - self.decorator.height()

        ctx = cairo.Context(surface)
        ctx.translate(self.decorator.left_width(), self.decorator.top_height())
        ctx.rectangle(0, 0, WIDTH, HEIGHT)
        ctx.set_source_rgb(204 / 255, 204 / 255, 204 / 255)
        ctx.fill()

        ctx.rectangle(0, 5 + self.menubar.height, WIDTH, self.tr.height - 10)
        ctx.set_source_rgb(1, 1, 1)
        ctx.fill()
        self.tr.resize(WIDTH - 10, self.tr.height)
        self.tr.draw(self)

        offset_x = 0
        offset_y = self.tr.height + self.menubar.height
        button_height = int((HEIGHT - self.tr.height - self.menubar.height) /
                            len(self.buttons))
        for row in self.buttons:
            button_width = int(WIDTH / len(row))
            for button in row:
                if button:
                    button.draw(self, ctx, offset_x, offset_y, button_width,
                                button_height)
                offset_x += button_width
            offset_x = 0
            offset_y += button_height

        self.menubar.draw(ctx, 0, 0, WIDTH)
        self.decorator.render(self)
        self.flip()

    def finish_resize(self, msg):
        """Accept a resize."""
        if msg.width < 200 or msg.height < 200:
            self.resize_offer(max(msg.width, 200), max(msg.height, 200))
            return
        self.resize_accept(msg.width, msg.height)
        self.reinit()
        self.draw()
        self.resize_done()
        self.flip()

    def mouse_event(self, msg):
        if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE:
            window.close()
            sys.exit(0)
        x, y = msg.new_x - self.decorator.left_width(
        ), msg.new_y - self.decorator.top_height()
        w, h = self.width - self.decorator.width(
        ), self.height - self.decorator.height()

        if x >= 0 and x < w and y >= 0 and y < self.menubar.height:
            self.menubar.mouse_event(msg, x, y)
            return

        redraw = False
        if self.down_button:
            if msg.command == yutani.MouseEvent.RAISE or msg.command == yutani.MouseEvent.CLICK:
                if not (msg.buttons & yutani.MouseButton.BUTTON_LEFT):
                    if x >= self.down_button.x and \
                        x < self.down_button.x + self.down_button.width and \
                        y >= self.down_button.y and \
                        y < self.down_button.y + self.down_button.height:
                        self.down_button.focus_enter()
                        self.down_button.callback(self.down_button)
                        self.down_button = None
                        redraw = True
                    else:
                        self.down_button.focus_leave()
                        self.down_button = None
                        redraw = True

        else:
            if y > self.tr.height + self.menubar.height and y < h and x >= 0 and x < w:
                row = int(
                    (y - self.tr.height - self.menubar.height) /
                    (self.height - self.decorator.height() - self.tr.height -
                     self.menubar.height) * len(self.buttons))
                col = int(x / (self.width - self.decorator.width()) *
                          len(self.buttons[row]))
                button = self.buttons[row][col]
                if button != self.hover_widget:
                    if button:
                        button.focus_enter()
                        redraw = True
                    if self.hover_widget:
                        self.hover_widget.focus_leave()
                        redraw = True
                    self.hover_widget = button

                if msg.command == yutani.MouseEvent.DOWN:
                    if button:
                        button.hilight = 2
                        self.down_button = button
                        redraw = True
            else:
                if self.hover_widget:
                    self.hover_widget.focus_leave()
                    redraw = True
                self.hover_widget = None

        if redraw:
            self.draw()

    def keyboard_event(self, msg):
        if msg.event.action != 0x01:
            return  # Ignore anything that isn't a key down.
        if msg.event.key in b"0123456789.+-/*()":
            self.add_string(msg.event.key.decode('utf-8'))
        if msg.event.key == b"\n":
            self.calculate()
        if msg.event.key == b"c":
            self.clear_text()
        if msg.event.keycode == 8:
            self.clear_last()
        if msg.event.key == b"q":
            self.close()
            sys.exit(0)
Esempio n. 8
0
    def __init__(self, decorator):
        super(MinesWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="mines", doublebuffer=True)
        self.move(100,100)
        self.decorator = decorator
        self.button_width = {}
        self.button_height = 0

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)
        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/mines.png",_description,"mines")
        def help_browser(action):
            subprocess.Popen(["help-browser.py","mines.trt"])
        def custom_game(action):
            def input_callback(input_window):
                size = int(input_window.tr.text)
                input_window.close()
                def second_callback(input_window):
                    mines = int(input_window.tr.text)
                    input_window.close()
                    self.new_game((size,mines))

                TextInputWindow(self.decorator,"How many mines?","mines",text="90",callback=second_callback,window=self)
            TextInputWindow(self.decorator,"How wide/tall?","mines",text="20",callback=input_callback,window=self)

        menus = [
            ("File", [
                MenuEntrySubmenu("New Game...",[
                    MenuEntryAction("9×9, 10 mines",None,self.new_game,(9,10)),
                    MenuEntryAction("16×16, 40 mines",None,self.new_game,(16,40)),
                    MenuEntryAction("20×20, 90 mines",None,self.new_game,(20,90)),
                    MenuEntryAction("Custom...",None,custom_game,None),
                ],icon="new"),
                MenuEntryDivider(),
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.tr = text_region.TextRegion(self.decorator.left_width()+5,self.decorator.top_height()+self.menubar.height,self.base_width-10,40)
        self.tr.set_font(toaru_fonts.Font(toaru_fonts.FONT_SANS_SERIF,18))
        self.tr.set_alignment(2)
        self.tr.set_valignment(2)
        self.tr.set_one_line()
        self.tr.set_ellipsis()


        self.error = False

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None
        self.modifiers = 0

        self.new_game((9,10))
Esempio n. 9
0
class MinesWindow(yutani.Window):

    base_width = 400
    base_height = 440

    def __init__(self, decorator):
        super(MinesWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="mines", doublebuffer=True)
        self.move(100,100)
        self.decorator = decorator
        self.button_width = {}
        self.button_height = 0

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)
        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/mines.png",_description,"mines")
        def help_browser(action):
            subprocess.Popen(["help-browser.py","mines.trt"])
        def custom_game(action):
            def input_callback(input_window):
                size = int(input_window.tr.text)
                input_window.close()
                def second_callback(input_window):
                    mines = int(input_window.tr.text)
                    input_window.close()
                    self.new_game((size,mines))

                TextInputWindow(self.decorator,"How many mines?","mines",text="90",callback=second_callback,window=self)
            TextInputWindow(self.decorator,"How wide/tall?","mines",text="20",callback=input_callback,window=self)

        menus = [
            ("File", [
                MenuEntrySubmenu("New Game...",[
                    MenuEntryAction("9×9, 10 mines",None,self.new_game,(9,10)),
                    MenuEntryAction("16×16, 40 mines",None,self.new_game,(16,40)),
                    MenuEntryAction("20×20, 90 mines",None,self.new_game,(20,90)),
                    MenuEntryAction("Custom...",None,custom_game,None),
                ],icon="new"),
                MenuEntryDivider(),
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.tr = text_region.TextRegion(self.decorator.left_width()+5,self.decorator.top_height()+self.menubar.height,self.base_width-10,40)
        self.tr.set_font(toaru_fonts.Font(toaru_fonts.FONT_SANS_SERIF,18))
        self.tr.set_alignment(2)
        self.tr.set_valignment(2)
        self.tr.set_one_line()
        self.tr.set_ellipsis()


        self.error = False

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None
        self.modifiers = 0

        self.new_game((9,10))

    def basic_game(self):
        self.new_game((9,10))

    def new_game(self,action):
        def mine_func(b):
            button = b
            if self.first_click:
                i = 0
                while button.is_mine or button.mines:
                    if i > 30:
                        DialogWindow(self.decorator,app_name,"Failed to generate a board.",callback=self.basic_game,window=self,icon='mines')
                        return
                    self.new_game(action)
                    button = self.buttons[button.row][button.col]
                    i += 1
                self.first_click = False
            if button.flagged:
                return
            if button.is_mine and not button.revealed:
                self.tr.set_text("You lose.")
                for row in self.buttons:
                    for b in row:
                        b.reveal()
                self.draw()
                def new():
                    self.new_game(action)
                DialogWindow(self.decorator,app_name,"Oops, you clicked a mine! Play another game?",callback=new,window=self,icon='mines')
                return
            else:
                if not button.revealed:
                    button.reveal()
                    if button.mines == 0:
                        n = [x for x in check_neighbor_buttons(button.row,button.col) if not x.revealed]
                        while n:
                            b = n.pop()
                            b.reveal()
                            if b.mines == 0:
                                n.extend([x for x in check_neighbor_buttons(b.row,b.col) if not x.revealed and not x in n])
                    self.check_win()

        self.field_size, self.mine_count = action
        self.first_click = True
        self.tr.set_text(f"There are {self.mine_count} mines.")

        self.mines = []
        i = 0
        while len(self.mines) < self.mine_count:
            x,y = random.randrange(self.field_size),random.randrange(self.field_size)
            i += 1
            if not (x,y) in self.mines:
                i = 0
                self.mines.append((x,y))
            if i > 50:
                DialogWindow(self.decorator,app_name,"Failed to generate a board.",callback=self.basic_game,window=self,icon='mines')
                return

        def check_neighbors(r,c):
            n = []
            if r > 0:
                if c > 0: n.append((r-1,c-1))
                n.append((r-1,c))
                if c < self.field_size-1: n.append((r-1,c+1))
            if r < self.field_size-1:
                if c > 0: n.append((r+1,c-1))
                n.append((r+1,c))
                if c < self.field_size-1: n.append((r+1,c+1))
            if c > 0: n.append((r,c-1))
            if c < self.field_size-1: n.append((r,c+1))
            return n

        def check_neighbor_buttons(r,c):
            return [self.buttons[x][y] for x,y in check_neighbors(r,c)]

        self.buttons = []
        for row in range(self.field_size):
            r = []
            for col in range(self.field_size):
                is_mine = (row,col) in self.mines
                neighbor_mines = len([x for x in check_neighbors(row,col) if x in self.mines])
                r.append(MineButton(mine_func,row,col,is_mine,neighbor_mines))
            self.buttons.append(r)


    def check_win(self):
        buttons = []
        for row in self.buttons:
            buttons.extend(row)
        n_flagged = len([x for x in buttons if x.flagged and not x.revealed])
        n_revealed = len([x for x in buttons if x.revealed])
        if n_flagged == self.mine_count and n_revealed + n_flagged == self.field_size ** 2:
            self.tr.set_text("You win!")
            for b in buttons:
                b.reveal()
            def new():
                self.new_game((self.field_size,self.mine_count))
            DialogWindow(self.decorator,app_name,"You won! Play another game?",callback=new,window=self,icon='mines')

    def flag(self,button):
        button.set_flagged()
        self.check_win()
        self.draw()

    def draw(self):
        surface = self.get_cairo_surface()

        WIDTH, HEIGHT = self.width - self.decorator.width(), self.height - self.decorator.height()

        ctx = cairo.Context(surface)
        ctx.translate(self.decorator.left_width(), self.decorator.top_height())
        ctx.rectangle(0,0,WIDTH,HEIGHT)
        ctx.set_source_rgb(204/255,204/255,204/255)
        ctx.fill()

        self.tr.resize(WIDTH-10, self.tr.height)
        self.tr.draw(self)

        offset_x = 0
        offset_y = self.tr.height + self.menubar.height
        self.button_height = int((HEIGHT - self.tr.height - self.menubar.height) / len(self.buttons))
        i = 0
        for row in self.buttons:
            self.button_width[i] = int(WIDTH / len(row))
            for button in row:
                if button:
                    button.draw(self,ctx,offset_x,offset_y,self.button_width[i],self.button_height)
                offset_x += self.button_width[i]
            offset_x = 0
            offset_y += self.button_height
            i += 1

        self.menubar.draw(ctx,0,0,WIDTH)
        self.decorator.render(self)
        self.flip()

    def finish_resize(self, msg):
        """Accept a resize."""
        if msg.width < 400 or msg.height < 400:
            self.resize_offer(max(msg.width,400),max(msg.height,400))
            return
        self.resize_accept(msg.width, msg.height)
        self.reinit()
        self.draw()
        self.resize_done()
        self.flip()

    def mouse_event(self, msg):
        if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE:
            window.close()
            sys.exit(0)
        x,y = msg.new_x - self.decorator.left_width(), msg.new_y - self.decorator.top_height()
        w,h = self.width - self.decorator.width(), self.height - self.decorator.height()

        if x >= 0 and x < w and y >= 0 and y < self.menubar.height:
            self.menubar.mouse_event(msg, x, y)
            return

        redraw = False
        if self.down_button:
            if msg.command == yutani.MouseEvent.RAISE or msg.command == yutani.MouseEvent.CLICK:
                if not (msg.buttons & yutani.MouseButton.BUTTON_LEFT):
                    if x >= self.down_button.x and \
                        x < self.down_button.x + self.down_button.width and \
                        y >= self.down_button.y and \
                        y < self.down_button.y + self.down_button.height:
                            self.down_button.focus_enter()
                            if self.modifiers & yutani.Modifier.MOD_LEFT_CTRL:
                                self.flag(self.down_button)
                            else:
                                self.down_button.callback(self.down_button)
                            self.down_button = None
                            redraw = True
                    else:
                        self.down_button.focus_leave()
                        self.down_button = None
                        redraw = True

        else:
            if y > self.tr.height + self.menubar.height and y < h and x >= 0 and x < w:
                xh = self.button_height * len(self.buttons)
                row = int((y - self.tr.height - self.menubar.height) / (xh) * len(self.buttons))
                if row < len(self.buttons):
                    xw = self.button_width[row] * len(self.buttons[row])
                    col = int(x / (xw) * len(self.buttons[row]))
                    if col < len(self.buttons[row]):
                        button = self.buttons[row][col]
                    else:
                        button = None
                else:
                    button = None
                if button != self.hover_widget:
                    if button:
                        button.focus_enter()
                        redraw = True
                    if self.hover_widget:
                        self.hover_widget.focus_leave()
                        redraw = True
                    self.hover_widget = button

                if msg.command == yutani.MouseEvent.DOWN:
                    if button:
                        button.hilight = 2
                        self.down_button = button
                        redraw = True
            else:
                if self.hover_widget:
                    self.hover_widget.focus_leave()
                    redraw = True
                self.hover_widget = None

        if redraw:
            self.draw()

    def keyboard_event(self, msg):
        self.modifiers = msg.event.modifiers
        if msg.event.action != 0x01:
            return # Ignore anything that isn't a key down.
        if msg.event.key == b"q":
            self.close()
            sys.exit(0)
Esempio n. 10
0
class MinesWindow(yutani.Window):

    base_width = 400
    base_height = 440

    def __init__(self, decorator):
        super(MinesWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="mines", doublebuffer=True)
        self.move(100,100)
        self.decorator = decorator
        self.button_width = {}
        self.button_height = 0

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)
        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/mines.png",_description,"mines")
        def help_browser(action):
            subprocess.Popen(["help-browser.py","mines.trt"])
        def custom_game(action):
            def input_callback(input_window):
                size = int(input_window.tr.text)
                input_window.close()
                def second_callback(input_window):
                    mines = int(input_window.tr.text)
                    input_window.close()
                    self.new_game((size,mines))

                TextInputWindow(self.decorator,"How many mines?","mines",text="90",callback=second_callback,window=self)
            TextInputWindow(self.decorator,"How wide/tall?","mines",text="20",callback=input_callback,window=self)

        menus = [
            ("File", [
                MenuEntrySubmenu("New Game...",[
                    MenuEntryAction("9×9, 10 mines",None,self.new_game,(9,10)),
                    MenuEntryAction("16×16, 40 mines",None,self.new_game,(16,40)),
                    MenuEntryAction("20×20, 90 mines",None,self.new_game,(20,90)),
                    MenuEntryAction("Custom...",None,custom_game,None),
                ],icon="new"),
                MenuEntryDivider(),
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.tr = text_region.TextRegion(self.decorator.left_width()+5,self.decorator.top_height()+self.menubar.height,self.base_width-10,40)
        self.tr.set_font(toaru_fonts.Font(toaru_fonts.FONT_SANS_SERIF,18))
        self.tr.set_alignment(2)
        self.tr.set_valignment(2)
        self.tr.set_one_line()
        self.tr.set_ellipsis()


        self.error = False

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None
        self.modifiers = 0

        self.new_game((9,10))

    def basic_game(self):
        self.new_game((9,10))

    def new_game(self,action):
        def mine_func(b):
            button = b
            if self.first_click:
                i = 0
                while button.is_mine or button.mines:
                    if i > 30:
                        DialogWindow(self.decorator,app_name,"Failed to generate a board.",callback=self.basic_game,window=self,icon='mines')
                        return
                    self.new_game(action)
                    button = self.buttons[button.row][button.col]
                    i += 1
                self.first_click = False
            if button.flagged:
                return
            if button.is_mine and not button.revealed:
                self.tr.set_text("You lose.")
                for row in self.buttons:
                    for b in row:
                        b.reveal()
                self.draw()
                def new():
                    self.new_game(action)
                DialogWindow(self.decorator,app_name,"Oops, you clicked a mine! Play another game?",callback=new,window=self,icon='mines')
                return
            else:
                if not button.revealed:
                    button.reveal()
                    if button.mines == 0:
                        n = [x for x in check_neighbor_buttons(button.row,button.col) if not x.revealed]
                        while n:
                            b = n.pop()
                            b.reveal()
                            if b.mines == 0:
                                n.extend([x for x in check_neighbor_buttons(b.row,b.col) if not x.revealed and not x in n])
                    self.check_win()

        self.field_size, self.mine_count = action
        self.first_click = True
        self.tr.set_text(f"There are {self.mine_count} mines.")

        self.mines = []
        i = 0
        while len(self.mines) < self.mine_count:
            x,y = random.randrange(self.field_size),random.randrange(self.field_size)
            i += 1
            if not (x,y) in self.mines:
                i = 0
                self.mines.append((x,y))
            if i > 50:
                DialogWindow(self.decorator,app_name,"Failed to generate a board.",callback=self.basic_game,window=self,icon='mines')
                return

        def check_neighbors(r,c):
            n = []
            if r > 0:
                if c > 0: n.append((r-1,c-1))
                n.append((r-1,c))
                if c < self.field_size-1: n.append((r-1,c+1))
            if r < self.field_size-1:
                if c > 0: n.append((r+1,c-1))
                n.append((r+1,c))
                if c < self.field_size-1: n.append((r+1,c+1))
            if c > 0: n.append((r,c-1))
            if c < self.field_size-1: n.append((r,c+1))
            return n

        def check_neighbor_buttons(r,c):
            return [self.buttons[x][y] for x,y in check_neighbors(r,c)]

        self.buttons = []
        for row in range(self.field_size):
            r = []
            for col in range(self.field_size):
                is_mine = (row,col) in self.mines
                neighbor_mines = len([x for x in check_neighbors(row,col) if x in self.mines])
                r.append(MineButton(mine_func,row,col,is_mine,neighbor_mines))
            self.buttons.append(r)


    def check_win(self):
        buttons = []
        for row in self.buttons:
            buttons.extend(row)
        n_flagged = len([x for x in buttons if x.flagged and not x.revealed])
        n_revealed = len([x for x in buttons if x.revealed])
        if n_flagged == self.mine_count and n_revealed + n_flagged == self.field_size ** 2:
            self.tr.set_text("You win!")
            for b in buttons:
                b.reveal()
            def new():
                self.new_game((self.field_size,self.mine_count))
            DialogWindow(self.decorator,app_name,"You won! Play another game?",callback=new,window=self,icon='mines')

    def flag(self,button):
        button.set_flagged()
        self.check_win()
        self.draw()

    def draw(self):
        surface = self.get_cairo_surface()

        WIDTH, HEIGHT = self.width - self.decorator.width(), self.height - self.decorator.height()

        ctx = cairo.Context(surface)
        ctx.translate(self.decorator.left_width(), self.decorator.top_height())
        ctx.rectangle(0,0,WIDTH,HEIGHT)
        ctx.set_source_rgb(204/255,204/255,204/255)
        ctx.fill()

        self.tr.resize(WIDTH-10, self.tr.height)
        self.tr.draw(self)

        offset_x = 0
        offset_y = self.tr.height + self.menubar.height
        self.button_height = int((HEIGHT - self.tr.height - self.menubar.height) / len(self.buttons))
        i = 0
        for row in self.buttons:
            self.button_width[i] = int(WIDTH / len(row))
            for button in row:
                if button:
                    button.draw(self,ctx,offset_x,offset_y,self.button_width[i],self.button_height)
                offset_x += self.button_width[i]
            offset_x = 0
            offset_y += self.button_height
            i += 1

        self.menubar.draw(ctx,0,0,WIDTH)
        self.decorator.render(self)
        self.flip()

    def finish_resize(self, msg):
        """Accept a resize."""
        if msg.width < 400 or msg.height < 400:
            self.resize_offer(max(msg.width,400),max(msg.height,400))
            return
        self.resize_accept(msg.width, msg.height)
        self.reinit()
        self.draw()
        self.resize_done()
        self.flip()

    def mouse_event(self, msg):
        if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE:
            window.close()
            sys.exit(0)
        x,y = msg.new_x - self.decorator.left_width(), msg.new_y - self.decorator.top_height()
        w,h = self.width - self.decorator.width(), self.height - self.decorator.height()

        if x >= 0 and x < w and y >= 0 and y < self.menubar.height:
            self.menubar.mouse_event(msg, x, y)
            return

        redraw = False
        if self.down_button:
            if msg.command == yutani.MouseEvent.RAISE or msg.command == yutani.MouseEvent.CLICK:
                if not (msg.buttons & yutani.MouseButton.BUTTON_LEFT):
                    if x >= self.down_button.x and \
                        x < self.down_button.x + self.down_button.width and \
                        y >= self.down_button.y and \
                        y < self.down_button.y + self.down_button.height:
                            self.down_button.focus_enter()
                            if self.modifiers & yutani.Modifier.MOD_LEFT_CTRL:
                                self.flag(self.down_button)
                            else:
                                self.down_button.callback(self.down_button)
                            self.down_button = None
                            redraw = True
                    else:
                        self.down_button.focus_leave()
                        self.down_button = None
                        redraw = True

        else:
            if y > self.tr.height + self.menubar.height and y < h and x >= 0 and x < w:
                xh = self.button_height * len(self.buttons)
                row = int((y - self.tr.height - self.menubar.height) / (xh) * len(self.buttons))
                if row < len(self.buttons):
                    xw = self.button_width[row] * len(self.buttons[row])
                    col = int(x / (xw) * len(self.buttons[row]))
                    if col < len(self.buttons[row]):
                        button = self.buttons[row][col]
                    else:
                        button = None
                else:
                    button = None
                if button != self.hover_widget:
                    if button:
                        button.focus_enter()
                        redraw = True
                    if self.hover_widget:
                        self.hover_widget.focus_leave()
                        redraw = True
                    self.hover_widget = button

                if msg.command == yutani.MouseEvent.DOWN:
                    if button:
                        button.hilight = 2
                        self.down_button = button
                        redraw = True
            else:
                if self.hover_widget:
                    self.hover_widget.focus_leave()
                    redraw = True
                self.hover_widget = None

        if redraw:
            self.draw()

    def keyboard_event(self, msg):
        self.modifiers = msg.event.modifiers
        if msg.event.action != 0x01:
            return # Ignore anything that isn't a key down.
        if msg.event.key == b"q":
            self.close()
            sys.exit(0)
Esempio n. 11
0
class CalculatorWindow(yutani.Window):

    base_width = 200
    base_height = 240

    def __init__(self, decorator):
        super(CalculatorWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="calculator", doublebuffer=True)
        self.move(100,100)
        self.decorator = decorator

        def add_string(button):
            self.add_string(button.text)

        def clear(button):
            self.clear_text()

        def calculate(button):
            self.calculate()

        self.buttons = [
            [Button("C",clear),      None,                   Button("(",add_string), Button(")",add_string)],
            [Button("7",add_string), Button("8",add_string), Button("9",add_string), Button("/",add_string)],
            [Button("4",add_string), Button("5",add_string), Button("6",add_string), Button("*",add_string)],
            [Button("1",add_string), Button("2",add_string), Button("3",add_string), Button("-",add_string)],
            [Button("0",add_string), Button(".",add_string), Button("=",calculate),  Button("+",add_string)],
        ]

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)
        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/calculator.png",_description,"calculator")
        def help_browser(action):
            subprocess.Popen(["help-browser.py","calculator.trt"])
        menus = [
            ("File", [
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.tr = text_region.TextRegion(self.decorator.left_width()+5,self.decorator.top_height()+self.menubar.height,self.base_width-10,40)
        self.tr.set_font(toaru_fonts.Font(toaru_fonts.FONT_MONOSPACE,18))
        self.tr.set_text("")
        self.tr.set_alignment(1)
        self.tr.set_valignment(2)
        self.tr.set_one_line()
        self.tr.set_ellipsis()

        self.error = False

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None


    def calculate(self):
        if self.error or len(self.tr.text) == 0:
            self.tr.set_text("0")
            self.error = False
        try:
            self.tr.set_text(str(eval_expr(self.tr.text)))
        except Exception as e:
            error = str(e)
            if "(" in error:
                error = error[:error.find("(")-1]
            self.tr.set_richtext(f"<i><color 0xFF0000>{e.__class__.__name__}</color>: {error}</i>")
            self.error = True
        self.draw()
        self.flip()

    def add_string(self, text):
        if self.error:
            self.tr.text = ""
            self.error = False
        self.tr.set_text(self.tr.text + text)
        self.draw()
        self.flip()

    def clear_text(self):
        self.error = False
        self.tr.set_text("")
        self.draw()
        self.flip()

    def clear_last(self):
        if self.error:
            self.error = False
            self.tr.set_text("")
        if len(self.tr.text):
            self.tr.set_text(self.tr.text[:-1])
        self.draw()
        self.flip()

    def draw(self):
        surface = self.get_cairo_surface()

        WIDTH, HEIGHT = self.width - self.decorator.width(), self.height - self.decorator.height()

        ctx = cairo.Context(surface)
        ctx.translate(self.decorator.left_width(), self.decorator.top_height())
        ctx.rectangle(0,0,WIDTH,HEIGHT)
        ctx.set_source_rgb(204/255,204/255,204/255)
        ctx.fill()

        ctx.rectangle(0,5+self.menubar.height,WIDTH,self.tr.height-10)
        ctx.set_source_rgb(1,1,1)
        ctx.fill()
        self.tr.resize(WIDTH-10, self.tr.height)
        self.tr.draw(self)

        offset_x = 0
        offset_y = self.tr.height + self.menubar.height
        button_height = int((HEIGHT - self.tr.height - self.menubar.height) / len(self.buttons))
        for row in self.buttons:
            button_width = int(WIDTH / len(row))
            for button in row:
                if button:
                    button.draw(self,ctx,offset_x,offset_y,button_width,button_height)
                offset_x += button_width
            offset_x = 0
            offset_y += button_height

        self.menubar.draw(ctx,0,0,WIDTH)
        self.decorator.render(self)
        self.flip()

    def finish_resize(self, msg):
        """Accept a resize."""
        if msg.width < 200 or msg.height < 200:
            self.resize_offer(max(msg.width,200),max(msg.height,200))
            return
        self.resize_accept(msg.width, msg.height)
        self.reinit()
        self.draw()
        self.resize_done()
        self.flip()

    def mouse_event(self, msg):
        if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE:
            window.close()
            sys.exit(0)
        x,y = msg.new_x - self.decorator.left_width(), msg.new_y - self.decorator.top_height()
        w,h = self.width - self.decorator.width(), self.height - self.decorator.height()

        if x >= 0 and x < w and y >= 0 and y < self.menubar.height:
            self.menubar.mouse_event(msg, x, y)
            return

        redraw = False
        if self.down_button:
            if msg.command == yutani.MouseEvent.RAISE or msg.command == yutani.MouseEvent.CLICK:
                if not (msg.buttons & yutani.MouseButton.BUTTON_LEFT):
                    if x >= self.down_button.x and \
                        x < self.down_button.x + self.down_button.width and \
                        y >= self.down_button.y and \
                        y < self.down_button.y + self.down_button.height:
                            self.down_button.focus_enter()
                            self.down_button.callback(self.down_button)
                            self.down_button = None
                            redraw = True
                    else:
                        self.down_button.focus_leave()
                        self.down_button = None
                        redraw = True

        else:
            if y > self.tr.height + self.menubar.height and y < h and x >= 0 and x < w:
                row = int((y - self.tr.height - self.menubar.height) / (self.height - self.decorator.height() - self.tr.height - self.menubar.height) * len(self.buttons))
                col = int(x / (self.width - self.decorator.width()) * len(self.buttons[row]))
                button = self.buttons[row][col]
                if button != self.hover_widget:
                    if button:
                        button.focus_enter()
                        redraw = True
                    if self.hover_widget:
                        self.hover_widget.focus_leave()
                        redraw = True
                    self.hover_widget = button

                if msg.command == yutani.MouseEvent.DOWN:
                    if button:
                        button.hilight = 2
                        self.down_button = button
                        redraw = True
            else:
                if self.hover_widget:
                    self.hover_widget.focus_leave()
                    redraw = True
                self.hover_widget = None

        if redraw:
            self.draw()

    def keyboard_event(self, msg):
        if msg.event.action != 0x01:
            return # Ignore anything that isn't a key down.
        if msg.event.key in b"0123456789.+-/*()":
            self.add_string(msg.event.key.decode('utf-8'))
        if msg.event.key == b"\n":
            self.calculate()
        if msg.event.key == b"c":
            self.clear_text()
        if msg.event.keycode == 8:
            self.clear_last()
        if msg.event.key == b"q":
            self.close()
            sys.exit(0)
Esempio n. 12
0
class FileBrowserWindow(yutani.Window):

    base_width = 400
    base_height = 300

    def __init__(self, decorator, path):
        super(FileBrowserWindow,
              self).__init__(self.base_width + decorator.width(),
                             self.base_height + decorator.height(),
                             title=app_name,
                             icon="folder",
                             doublebuffer=True)
        self.move(100, 100)
        self.x = 100
        self.y = 100
        self.decorator = decorator

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)

        def about_window(action):
            AboutAppletWindow(self.decorator, f"About {app_name}",
                              "/usr/share/icons/48/folder.png", _description,
                              "folder")

        def help_browser(action):
            subprocess.Popen(["help-browser.py", "file_browser.trt"])

        def input_path(action):
            def input_callback(input_window):
                text = input_window.tr.text
                input_window.close()
                self.load_directory(text)

            TextInputWindow(self.decorator,
                            "Open directory...",
                            "open",
                            text=self.path,
                            callback=input_callback,
                            window=self)

        menus = [
            ("File", [
                MenuEntryAction("Exit", "exit", exit_app, None),
            ]),
            ("Go", [
                MenuEntryAction("Path...", "open", input_path, None),
                MenuEntryDivider(),
                MenuEntryAction("Home", "home", self.load_directory,
                                os.environ.get("HOME")),
                MenuEntryAction("File System", None, self.load_directory, "/"),
                MenuEntryAction("Up", "up", self.go_up, None),
            ]),
            ("Help", [
                MenuEntryAction("Contents", "help", help_browser, None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}", "star", about_window,
                                None),
            ]),
        ]

        self.menubar = MenuBarWidget(self, menus)

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None

        self.buf = None
        self.load_directory(path)
        self.hilighted = None

    def go_up(self, action):
        self.load_directory(os.path.abspath(os.path.join(self.path, '..')))
        self.draw()

    def load_directory(self, path):
        if not os.path.exists(path):
            DialogWindow(
                self.decorator,
                app_name,
                f"The path <mono>{path}</mono> could not be opened. (Not found)",
                window=self,
                icon='folder')
            return
        if not os.path.isdir(path):
            DialogWindow(
                self.decorator,
                app_name,
                f"The path <mono>{path}</mono> could not be opened. (Not a directory)",
                window=self,
                icon='folder')
            return
        path = os.path.normpath(path)
        self.path = path
        title = "/" if path == "/" else os.path.basename(path)
        self.set_title(f"{title} - {app_name}", 'folder')

        self.files = sorted(
            [File(os.path.join(path, f), self) for f in os.listdir(path)],
            key=lambda x: x.sortkey)
        self.scroll_y = 0
        self.hilighted = None
        self.redraw_buf()

    def redraw_buf(self, icons=None):
        if self.buf:
            self.buf.destroy()
        w = self.width - self.decorator.width()
        files_per_row = int(w / 100)
        self.buf = yutani.GraphicsBuffer(
            w,
            math.ceil(len(self.files) / files_per_row) * 100)

        surface = self.buf.get_cairo_surface()
        ctx = cairo.Context(surface)

        if icons:
            for icon in icons:
                ctx.rectangle(icon.x, icon.y, 100, 100)
            ctx.clip()

        ctx.rectangle(0, 0, surface.get_width(), surface.get_height())
        ctx.set_source_rgb(1, 1, 1)
        ctx.fill()

        offset_x = 0
        offset_y = 0

        for f in self.files:
            if not icons or f in icons:
                x_, y_ = ctx.user_to_device(0, 0)
                f.tr.move(offset_x, offset_y + 60)
                f.tr.draw(self.buf)
                ctx.set_source_surface(f.icon, offset_x + 26, offset_y + 10)
                ctx.paint_with_alpha(1.0 if not f.hilight else 0.7)
            f.x = offset_x
            f.y = offset_y
            offset_x += 100
            if offset_x + 100 > surface.get_width():
                offset_x = 0
                offset_y += 100

    def draw(self):
        surface = self.get_cairo_surface()

        WIDTH, HEIGHT = self.width - self.decorator.width(
        ), self.height - self.decorator.height()

        ctx = cairo.Context(surface)
        ctx.translate(self.decorator.left_width(), self.decorator.top_height())
        ctx.rectangle(0, 0, WIDTH, HEIGHT)
        ctx.set_source_rgb(1, 1, 1)
        ctx.fill()

        ctx.save()
        ctx.translate(0, self.menubar.height)
        text = self.buf.get_cairo_surface()
        ctx.set_source_surface(text, 0, self.scroll_y)
        ctx.paint()
        ctx.restore()

        self.menubar.draw(ctx, 0, 0, WIDTH)

        self.decorator.render(self)
        self.flip()

    def finish_resize(self, msg):
        """Accept a resize."""
        if msg.width < 120 or msg.height < 120:
            self.resize_offer(max(msg.width, 120), max(msg.height, 120))
            return
        self.resize_accept(msg.width, msg.height)
        self.reinit()
        self.redraw_buf()
        self.draw()
        self.resize_done()
        self.flip()

    def scroll(self, amount):
        w, h = self.width - self.decorator.width(
        ), self.height - self.decorator.height()
        files_per_row = int(w / 100)
        rows_total = math.ceil(len(self.files) / files_per_row)
        rows_visible = int((h - 24) / 100)
        rows = rows_total - rows_visible
        if rows < 0: rows = 0
        self.scroll_y += amount
        if self.scroll_y > 0:
            self.scroll_y = 0
        if self.scroll_y < -100 * rows:
            self.scroll_y = -100 * rows

    def mouse_event(self, msg):
        if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE:
            window.close()
            sys.exit(0)
        x, y = msg.new_x - self.decorator.left_width(
        ), msg.new_y - self.decorator.top_height()
        w, h = self.width - self.decorator.width(
        ), self.height - self.decorator.height()

        if x >= 0 and x < w and y >= 0 and y < self.menubar.height:
            self.menubar.mouse_event(msg, x, y)
            return

        if msg.buttons & yutani.MouseButton.SCROLL_UP:
            self.scroll(30)
            self.draw()
            return
        elif msg.buttons & yutani.MouseButton.SCROLL_DOWN:
            self.scroll(-30)
            self.draw()
            return

        if x >= 0 and x < w and y >= self.menubar.height and y < h:
            if msg.buttons & yutani.MouseButton.BUTTON_RIGHT:
                if not self.menus:
                    menu_entries = [
                        MenuEntryAction("Up", "up", self.go_up, None),
                    ]
                    menu = MenuWindow(menu_entries,
                                      (self.x + msg.new_x, self.y + msg.new_y),
                                      root=self)
                return

        if y < 0: return

        offset_x = 0
        offset_y = self.scroll_y + self.menubar.height

        redraw = []

        files_per_row = int(w / 100)
        rows_total = math.ceil(len(self.files) / files_per_row)
        skip_files = files_per_row * (int(-offset_y / 100))
        offset_y += int(-offset_y / 100) * 100

        hit = False
        for f in self.files[skip_files:]:
            if offset_y > h: break
            if offset_y > -100:
                if x >= offset_x and x < offset_x + 100 and y >= offset_y and y < offset_y + 100:
                    if not f.hilight:
                        redraw.append(f)
                        if self.hilighted:
                            redraw.append(self.hilighted)
                            self.hilighted.hilight = False
                        f.hilight = True
                    self.hilighted = f
                    hit = True
                    break
            offset_x += 100
            if offset_x + 100 > w:
                offset_x = 0
                offset_y += 100
        if not hit:
            if self.hilighted:
                redraw.append(self.hilighted)
                self.hilighted.hilight = False
                self.hilighted = None

        if self.hilighted:
            if msg.command == yutani.MouseEvent.DOWN:
                self.hilighted.do_action()

        if redraw:
            self.redraw_buf(redraw)
            self.draw()

    def keyboard_event(self, msg):
        if msg.event.action != yutani.KeyAction.ACTION_DOWN:
            return  # Ignore anything that isn't a key down.
        if msg.event.key == b"q":
            self.close()
            sys.exit(0)
Esempio n. 13
0
class PackageManagerWindow(yutani.Window):

    base_width = 400
    base_height = 300

    def __init__(self, decorator):
        super(PackageManagerWindow,
              self).__init__(self.base_width + decorator.width(),
                             self.base_height + decorator.height(),
                             title="Package Manager",
                             icon="package",
                             doublebuffer=True)
        self.move(100, 100)
        self.x = 100
        self.y = 100
        self.decorator = decorator

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)

        def about_window(action):
            AboutAppletWindow(self.decorator, f"About Package Manager",
                              "/usr/share/icons/48/package.png", _description,
                              "package")

        def help_browser(action):
            subprocess.Popen(["help-browser.py", "packages.trt"])

        menus = [
            ("File", [
                MenuEntryAction("Exit", "exit", exit_app, None),
            ]),
            ("Help", [
                MenuEntryAction("Contents", "help", help_browser, None),
                MenuEntryDivider(),
                MenuEntryAction("About Package Manager", "star", about_window,
                                None),
            ]),
        ]

        self.menubar = MenuBarWidget(self, menus)

        self.menus = {}
        self.hovered_menu = None

        self.scroll_y = 0
        self.hilighted = None
        self.buf = None
        self.hilighted = None

    def load_packages(self):
        self.packages = sorted([
            Package(name)
            for name in toaru_package.manifest['packages'].keys()
        ],
                               key=lambda x: x.name)

    def redraw_buf(self, clips=None):
        if self.buf:
            self.buf.destroy()
        w = self.width - self.decorator.width()
        self.buf = yutani.GraphicsBuffer(w, len(self.packages) * 24)

        surface = self.buf.get_cairo_surface()
        ctx = cairo.Context(surface)

        if clips:
            for clip in clips:
                ctx.rectangle(clip.x, clip.y, w, 24)
            ctx.clip()

        ctx.rectangle(0, 0, surface.get_width(), surface.get_height())
        ctx.set_source_rgb(1, 1, 1)
        ctx.fill()

        offset_y = 0

        for f in self.packages:
            f.y = offset_y
            if not clips or f in clips:
                tr = text_region.TextRegion(4, offset_y + 4, w - 4, 20)
                if f.hilight:
                    gradient = cairo.LinearGradient(0, 0, 0, 18)
                    gradient.add_color_stop_rgba(0.0, *hilight_gradient_top,
                                                 1.0)
                    gradient.add_color_stop_rgba(1.0, *hilight_gradient_bottom,
                                                 1.0)
                    ctx.rectangle(0, offset_y + 4, w, 1)
                    ctx.set_source_rgb(*hilight_border_top)
                    ctx.fill()
                    ctx.rectangle(0, offset_y + 4 + 20 - 1, w, 1)
                    ctx.set_source_rgb(*hilight_border_bottom)
                    ctx.fill()
                    ctx.save()
                    ctx.translate(0, offset_y + 4 + 1)
                    ctx.rectangle(0, 0, w, 20 - 2)
                    ctx.set_source(gradient)
                    ctx.fill()
                    ctx.restore()
                    tr.font.font_color = 0xFFFFFFFF
                else:
                    ctx.rectangle(0, offset_y + 4, w, 20)
                    ctx.set_source_rgb(1, 1, 1)
                    ctx.fill()
                    tr.font.font_color = 0xFF000000
                tr.set_richtext(f.text)
                tr.set_one_line()
                tr.set_ellipsis()
                tr.draw(self.buf)
            offset_y += 24

    def draw(self):
        surface = self.get_cairo_surface()

        WIDTH, HEIGHT = self.width - self.decorator.width(
        ), self.height - self.decorator.height()

        ctx = cairo.Context(surface)
        ctx.translate(self.decorator.left_width(), self.decorator.top_height())
        ctx.rectangle(0, 0, WIDTH, HEIGHT)
        ctx.set_source_rgb(1, 1, 1)
        ctx.fill()

        ctx.save()
        ctx.translate(0, self.menubar.height)
        text = self.buf.get_cairo_surface()
        ctx.set_source_surface(text, 0, self.scroll_y)
        ctx.paint()
        ctx.restore()

        self.menubar.draw(ctx, 0, 0, WIDTH)

        self.decorator.render(self)
        self.flip()

    def finish_resize(self, msg):
        """Accept a resize."""
        if msg.width < 120 or msg.height < 120:
            self.resize_offer(max(msg.width, 120), max(msg.height, 120))
            return
        self.resize_accept(msg.width, msg.height)
        self.reinit()
        self.redraw_buf()
        self.draw()
        self.resize_done()
        self.flip()

    def scroll(self, amount):
        w, h = self.width - self.decorator.width(
        ), self.height - self.decorator.height()
        rows = 1000
        self.scroll_y += amount
        if self.scroll_y > 0:
            self.scroll_y = 0
        if self.scroll_y < -100 * rows:
            self.scroll_y = -100 * rows

    def mouse_event(self, msg):
        if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE:
            window.close()
            sys.exit(0)
        x, y = msg.new_x - self.decorator.left_width(
        ), msg.new_y - self.decorator.top_height()
        w, h = self.width - self.decorator.width(
        ), self.height - self.decorator.height()

        if x >= 0 and x < w and y >= 0 and y < self.menubar.height:
            self.menubar.mouse_event(msg, x, y)
            return

        if x < 0 or x >= w or y < 0 or y >= h:
            return

        if msg.buttons & yutani.MouseButton.SCROLL_UP:
            self.scroll(30)
            self.draw()
            return
        elif msg.buttons & yutani.MouseButton.SCROLL_DOWN:
            self.scroll(-30)
            self.draw()
            return

        if x >= 0 and x < w and y >= self.menubar.height and y < h:
            if msg.buttons & yutani.MouseButton.BUTTON_RIGHT:
                if not self.menus:
                    pass  # no context menu at the moment
                    #menu_entries = [
                    #    MenuEntryAction("Up",None,self.go_up,None),
                    #]
                    #menu = MenuWindow(menu_entries,(self.x+msg.new_x,self.y+msg.new_y),root=self)
                return

        if y < 0: return

        offset_y = self.scroll_y + self.menubar.height

        redraw = []
        hit = False

        for f in self.packages:
            if offset_y > h: break
            if y >= offset_y and y < offset_y + 24:
                if not f.hilight:
                    redraw.append(f)
                    if self.hilighted:
                        redraw.append(self.hilighted)
                        self.hilighted.hilight = False
                    f.hilight = True
                self.hilighted = f
                hit = True
                break
            offset_y += 24

        if not hit:
            if self.hilighted:
                redraw.append(self.hilighted)
                self.hilighted.hilight = False
                self.hilighted = None

        if self.hilighted:
            if msg.command == yutani.MouseEvent.DOWN:
                if self.hilighted.do_action():
                    redraw = []
                    self.redraw_buf()
                    self.draw()

        if redraw:
            self.redraw_buf(redraw)
            self.draw()

    def keyboard_event(self, msg):
        if msg.event.action != yutani.KeyAction.ACTION_DOWN:
            return  # Ignore anything that isn't a key down.
        if msg.event.key == b"q":
            self.close()
            sys.exit(0)
Esempio n. 14
0
class PaintingWindow(yutani.Window):

    base_width = 600
    base_height = 600

    def __init__(self, decorator, path):
        super(PaintingWindow,
              self).__init__(self.base_width + decorator.width(),
                             self.base_height + decorator.height(),
                             title=app_name,
                             icon="applications-painting",
                             doublebuffer=True)
        self.move(100, 100)
        self.x = 100
        self.y = 100
        self.decorator = decorator
        self.picker = None
        self.last_color = (0, 0, 0)
        self.modifiers = None
        self.checkpattern = self.checkerboard(24)

        def about_window(action):
            AboutAppletWindow(self.decorator, f"About {app_name}",
                              "/usr/share/icons/48/applications-painting.png",
                              _description, "applications-painting")

        def help_browser(action):
            subprocess.Popen(["help-browser.py", "painting.trt"])

        def close_picker():
            self.last_color = self.picker.color
            self.picker = None

        def open_file(action):
            OpenFileDialog(self.decorator,
                           "Open...",
                           glob="*.png",
                           callback=self.load_buffer,
                           window=self)

        def new_surface(action):
            # TODO: prompt for size
            if self.buf:
                self.buf.destroy()
            self.new_buffer(*action)
            self.draw()

        def new_prompt(action):
            def input_callback(input_window):
                width = int(input_window.tr.text)
                input_window.close()

                def second_callback(input_window):
                    height = int(input_window.tr.text)
                    input_window.close()
                    new_surface((width, height))

                TextInputWindow(self.decorator,
                                "Height?",
                                "new",
                                text="500",
                                callback=second_callback,
                                window=self)

            TextInputWindow(self.decorator,
                            "Width?",
                            "new",
                            text="500",
                            callback=input_callback,
                            window=self)

        def save_file(action):
            self.modified = False
            path = '/tmp/painting.png'
            self.set_title(f'{os.path.basename(path)} - {app_name}', self.icon)
            self.surface.write_to_png(path)

        def select_color(action):
            if self.picker:
                return
            else:
                self.picker = ColorPickerWindow(self.decorator, close_picker)
                self.picker.draw()

        def clear_everything(action):
            self.draw_ctx.save()
            self.draw_ctx.set_operator(cairo.OPERATOR_SOURCE)
            self.draw_ctx.rectangle(0, 0, self.surface.get_width(),
                                    self.surface.get_height())
            self.draw_ctx.set_source_rgba(0, 0, 0, 0)
            self.draw_ctx.fill()
            self.draw_ctx.restore()

        menus = [
            ("File", [
                MenuEntrySubmenu("New...", [
                    MenuEntryAction("500×500", "new", new_surface, (500, 500)),
                    MenuEntryAction("800×600", "new", new_surface, (800, 600)),
                    MenuEntryAction("Custom...", "new", new_prompt, None),
                ],
                                 icon="new"),
                MenuEntryAction("Open", "open", open_file, None),
                MenuEntryAction("Save", "save", save_file, None),
                MenuEntryDivider(),
                MenuEntryAction("Exit", "exit", self.exit_app, None),
            ]),
            ("Tools", [
                MenuEntryAction("Color", None, select_color, None),
                MenuEntryAction("Clear Everything", None, clear_everything,
                                None),
            ]),
            ("Help", [
                MenuEntryAction("Contents", "help", help_browser, None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}", "star", about_window,
                                None),
            ]),
        ]

        self.menubar = MenuBarWidget(self, menus)

        self.menus = {}
        self.hovered_menu = None

        if not path:
            self.new_buffer(500, 500)
        else:
            self.load_buffer(path)

        self.hilighted = None
        self.was_drawing = False
        self.line_width = 2.0
        self.curs_x = None
        self.curs_y = None
        self.moving = False
        self.scale = 1.0
        self.modified = False

    def actually_exit(self):
        menus = [x for x in self.menus.values()]
        for x in menus:
            x.definitely_close()
        self.close()
        sys.exit(0)

    def exit_app(self, action=None):
        if self.modified:
            DialogWindow(
                self.decorator,
                app_name,
                "You have unsaved changes.\nAre you sure you want to quit?",
                callback=self.actually_exit,
                window=self)
        else:
            self.actually_exit()

    def load_buffer(self, path):
        self.set_title(f'{os.path.basename(path)} - {app_name}', self.icon)
        s = cairo.ImageSurface.create_from_png(path)

        self.init_buffer(s.get_width(), s.get_height())
        self.draw_ctx.save()
        self.draw_ctx.set_operator(cairo.OPERATOR_SOURCE)
        self.draw_ctx.rectangle(0, 0, self.surface.get_width(),
                                self.surface.get_height())
        self.draw_ctx.set_source_rgba(0, 0, 0, 0)
        self.draw_ctx.fill()
        self.draw_ctx.restore()
        self.draw_ctx.set_source_surface(s, 0, 0)
        self.draw_ctx.paint()

    def init_buffer(self, w, h):
        self.modified = False
        self.buf = yutani.GraphicsBuffer(w, h)
        self.surface = self.buf.get_cairo_surface()
        self.draw_ctx = cairo.Context(self.surface)
        self.offset_x = int(
            (self.width - self.decorator.width() - self.buf.width) / 2)
        self.offset_y = int((self.height - self.decorator.height() -
                             self.buf.height - self.menubar.height) / 2)

    def new_buffer(self, w, h):
        self.init_buffer(w, h)
        self.set_title(f'Untitled - {app_name}', self.icon)
        self.draw_ctx.rectangle(0, 0, self.surface.get_width(),
                                self.surface.get_height())
        self.draw_ctx.set_source_rgb(1, 1, 1)
        self.draw_ctx.fill()

    def color(self):
        if self.picker:
            return self.picker.color
        else:
            return self.last_color

    def checkerboard(self, size):
        s = cairo.ImageSurface(cairo.FORMAT_ARGB32, size, size)
        c = cairo.Context(s)
        c.set_source_rgb(128 / 255, 128 / 255, 128 / 255)
        c.paint()
        c.set_source_rgb(200 / 255, 200 / 255, 200 / 255)
        c.rectangle(size / 2, 0, size / 2, size / 2)
        c.rectangle(0, size / 2, size / 2, size / 2)
        c.fill()
        return s

    def draw(self, decor=True, menu=True, clips=None):
        surface = self.get_cairo_surface()

        WIDTH, HEIGHT = self.width - self.decorator.width(
        ), self.height - self.decorator.height()

        ctx = cairo.Context(surface)
        ctx.translate(self.decorator.left_width(), self.decorator.top_height())

        if clips:
            for clip in clips:
                x, y, a, b = clip
                width = a - x
                height = b - y
                ctx.rectangle(x, y, width, height)
            ctx.clip()

        ctx.save()
        ctx.set_source_surface(self.checkpattern, 0, 0)
        ctx.get_source().set_filter(cairo.FILTER_NEAREST)
        ctx.get_source().set_extend(cairo.EXTEND_REPEAT)
        ctx.rectangle(0, self.menubar.height, WIDTH,
                      HEIGHT - self.menubar.height)
        ctx.fill()
        ctx.restore()

        ctx.save()
        ctx.translate(0, self.menubar.height)
        ctx.rectangle(0, 0, WIDTH, HEIGHT - self.menubar.height)
        ctx.clip()

        ctx.save()
        ctx.scale(self.scale, self.scale)
        ctx.set_source_surface(self.surface, self.offset_x / self.scale,
                               self.offset_y / self.scale)
        ctx.get_source().set_filter(cairo.FILTER_FAST)
        ctx.paint()

        if not self.curs_x is None:
            if self.scale < 1.0:
                ctx.set_line_width(0.5 / self.scale)
            else:
                ctx.set_line_width(0.5)
            ctx.arc(self.curs_x / self.scale, self.curs_y / self.scale,
                    self.line_width / 2, 0, 2 * math.pi)
            ctx.set_source_rgba(0, 0, 0, 0.7)
            ctx.stroke()
            ctx.arc(self.curs_x / self.scale, self.curs_y / self.scale,
                    self.line_width / 2 - 0.5, 0, 2 * math.pi)
            ctx.set_source_rgba(1, 1, 1, 0.7)
            ctx.stroke()

        ctx.restore()
        ctx.restore()

        # For debugging clip regions.
        #if clips:
        #    for clip in clips:
        #        x,y,a,b = clip
        #        width = a - x
        #        height = b - y
        #        ctx.rectangle(x,y,width,height)
        #        ctx.set_source_rgba(1.0,0,0,0.4)
        #        ctx.paint()

        if menu:
            self.menubar.draw(ctx, 0, 0, WIDTH)

        if decor:
            self.decorator.render(self)

        self.flip()

    def finish_resize(self, msg):
        """Accept a resize."""
        if msg.width < 120 or msg.height < 120:
            self.resize_offer(max(msg.width, 120), max(msg.height, 120))
            return
        self.resize_accept(msg.width, msg.height)
        self.reinit()
        self.offset_x = int(
            (self.width - self.decorator.width() - self.buf.width) / 2)
        self.offset_y = int((self.height - self.decorator.height() -
                             self.buf.height - self.menubar.height) / 2)
        self.draw()
        self.resize_done()
        self.flip()

    def get_color(self, x, y):
        c = self.buf.get_value(x, y)
        a = (c >> 24) & 0xFF
        r = (c >> 16) & 0xFF
        g = (c >> 8) & 0xFF
        b = (c) & 0xFF
        return (r, g, b)

    def mouse_event(self, msg):
        if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE:
            self.exit_app()
        x, y = msg.new_x - self.decorator.left_width(
        ), msg.new_y - self.decorator.top_height()
        w, h = self.width - self.decorator.width(
        ), self.height - self.decorator.height()

        if not self.was_drawing:
            if x >= 0 and x < w and y >= 0 and y < self.menubar.height:
                self.menubar.mouse_event(msg, x, y)
                return

            if x < 0 or x >= w or y < 0 or y >= h:
                return

            if x >= 0 and x < w and y >= self.menubar.height and y < h:
                if msg.buttons & yutani.MouseButton.BUTTON_RIGHT:
                    if self.picker:
                        _x, _y = x - self.offset_x, y - self.menubar.height - self.offset_y
                        if _x >= 0 and _x < self.surface.get_width(
                        ) and _y >= 0 and _y < self.surface.get_height():
                            self.picker.set_color(*self.get_color(_x, _y))
                    if not self.menus:
                        pass  # No context menu at the moment.
                        #menu_entries = [
                        #    MenuEntryAction("Up",None,self.go_up,None),
                        #]
                        #menu = MenuWindow(menu_entries,(self.x+msg.new_x,self.y+msg.new_y),root=self)
                    return

            if y < 0: return

        decor = False
        regions = []

        if not self.modifiers:
            if msg.buttons & yutani.MouseButton.SCROLL_UP:
                self.line_width *= 1.2
            elif msg.buttons & yutani.MouseButton.SCROLL_DOWN:
                self.line_width /= 1.2
        elif self.modifiers & yutani.Modifier.MOD_LEFT_CTRL:
            if msg.buttons & yutani.MouseButton.SCROLL_UP:
                self.scale += 0.1
                regions.append((0, 0, w, h))
            elif msg.buttons & yutani.MouseButton.SCROLL_DOWN:
                self.scale -= 0.1
                if self.scale < 0.1:
                    self.scale = 0.1
                regions.append((0, 0, w, h))

        if not (msg.buttons & yutani.MouseButton.BUTTON_LEFT):
            self.was_drawing = False

        if (msg.buttons & yutani.MouseButton.BUTTON_MIDDLE):
            if not self.moving:
                self.initial = msg.new_x, msg.new_y
                self.initial_off = self.offset_x, self.offset_y
                self.moving = True
            regions.append((self.offset_x, self.offset_y + self.menubar.height,
                            self.offset_x + int(self.buf.width * self.scale),
                            self.offset_y + self.menubar.height +
                            int(self.buf.height * self.scale)))
            self.offset_x = self.initial_off[0] + msg.new_x - self.initial[0]
            self.offset_y = self.initial_off[1] + msg.new_y - self.initial[1]
            regions.append((self.offset_x, self.offset_y + self.menubar.height,
                            self.offset_x + int(self.buf.width * self.scale),
                            self.offset_y + self.menubar.height +
                            int(self.buf.height * self.scale)))
        else:
            self.moving = False

        cur_x_new = msg.new_x - self.decorator.left_width()
        cur_y_new = msg.new_y - self.decorator.top_height()

        cur_x_old = msg.old_x - self.decorator.left_width()
        cur_y_old = msg.old_y - self.decorator.top_height()

        if (msg.command == yutani.MouseEvent.DRAG
                or msg.command == yutani.MouseEvent.DOWN
            ) and msg.buttons & yutani.MouseButton.BUTTON_LEFT:
            self.was_drawing = True
            self.draw_ctx.set_line_cap(cairo.LINE_CAP_ROUND)
            self.draw_ctx.set_line_join(cairo.LINE_JOIN_ROUND)
            self.draw_ctx.set_source_rgb(*self.color())
            self.draw_ctx.set_line_width(self.line_width)

            x_1 = 0.5 + (cur_x_new - self.offset_x) / self.scale
            y_1 = 0.5 + (cur_y_new - self.offset_y -
                         self.menubar.height) / self.scale
            if msg.command == yutani.MouseEvent.DOWN:
                x_0 = x_1
                y_0 = y_1
                regions.append((
                    int(cur_x_new - self.line_width * self.scale),
                    int(cur_y_new - self.line_width * self.scale),
                    int(cur_x_new + self.line_width * self.scale),
                    int(cur_y_new + self.line_width * self.scale),
                ))
            else:
                x_0 = 0.5 + (cur_x_old - self.offset_x) / self.scale
                y_0 = 0.5 + (cur_y_old - self.offset_y -
                             self.menubar.height) / self.scale
                regions.append((
                    int(
                        min(cur_x_new, cur_x_old) -
                        self.line_width * self.scale),
                    int(
                        min(cur_y_new, cur_y_old) -
                        self.line_width * self.scale),
                    int(
                        max(cur_x_new, cur_x_old) +
                        self.line_width * self.scale),
                    int(
                        max(cur_y_new, cur_y_old) +
                        self.line_width * self.scale),
                ))

            self.draw_ctx.move_to(x_0, y_0)
            self.draw_ctx.line_to(x_1, y_1)
            self.draw_ctx.stroke()
            if not self.modified:
                self.modified = True
                self.set_title("*" + self.title, self.icon)
                decor = True
        else:
            regions.append((
                int(cur_x_new - self.line_width * self.scale),
                int(cur_y_new - self.line_width * self.scale),
                int(cur_x_new + self.line_width * self.scale),
                int(cur_y_new + self.line_width * self.scale),
            ))

        if self.curs_x:
            regions.append((
                int(self.curs_x - self.line_width * self.scale - 1.0),
                int(self.curs_y - self.line_width * self.scale +
                    self.menubar.height - 1.0),
                int(self.curs_x + self.line_width * self.scale + 1.0),
                int(self.curs_y + self.line_width * self.scale +
                    self.menubar.height + 1.0),
            ))
        self.curs_x = 0.5 + msg.new_x - self.decorator.left_width()
        self.curs_y = 0.5 + msg.new_y - self.decorator.top_height(
        ) - self.menubar.height
        self.draw(menu=False, decor=decor, clips=regions)

    def keyboard_event(self, msg):
        self.modifiers = msg.event.modifiers
        if msg.event.action != yutani.KeyAction.ACTION_DOWN:
            return  # Ignore anything that isn't a key down.
        if msg.event.key == b"q":
            self.exit_app()
Esempio n. 15
0
class PackageManagerWindow(yutani.Window):

    base_width = 640
    base_height = 480

    def __init__(self, decorator):
        super(PackageManagerWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="package", doublebuffer=True)
        self.move(100,100)
        self.x = 100
        self.y = 100
        self.decorator = decorator

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)
        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/package.png",_description,"package")
        def help_browser(action):
            subprocess.Popen(["help-browser.py","packages.trt"])
        def refresh_index(action):
            global _manifest
            msk.fetch_manifest()
            _manifest = msk.get_manifest()
            self.redraw_buf()
            self.draw()
        menus = [
            ("File", [
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Index", [
                MenuEntryAction("Refresh","refresh",refresh_index,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.menus = {}
        self.hovered_menu = None

        self.scroll_y = 0
        self.hilighted = None
        self.buf = None
        self.hilighted = None

    def load_packages(self):
        self.packages = sorted([Package(name) for name in _manifest.keys()],key=lambda x: x.name)

    def redraw_buf(self,clips=None):
        if self.buf:
            self.buf.destroy()
        w = self.width - self.decorator.width()
        self.buf = yutani.GraphicsBuffer(w,len(self.packages)*package_height)

        surface = self.buf.get_cairo_surface()
        ctx = cairo.Context(surface)

        if clips:
            for clip in clips:
                ctx.rectangle(clip.x,clip.y,w,package_height)
            ctx.clip()

        ctx.rectangle(0,0,surface.get_width(),surface.get_height())
        ctx.set_source_rgb(1,1,1)
        ctx.fill()

        offset_y = 0

        button = Button('Install', None)

        for f in self.packages:
            f.y = offset_y
            if not clips or f in clips:
                tr = text_region.TextRegion(64,offset_y+4,w-64 - 120,package_height-4)
                tr.line_height = 19
                if False and f.hilight:
                    gradient = cairo.LinearGradient(0,0,0,18)
                    gradient.add_color_stop_rgba(0.0,*hilight_gradient_top,1.0)
                    gradient.add_color_stop_rgba(1.0,*hilight_gradient_bottom,1.0)
                    ctx.rectangle(0,offset_y+4,w,1)
                    ctx.set_source_rgb(*hilight_border_top)
                    ctx.fill()
                    ctx.rectangle(0,offset_y+package_height-1,w,1)
                    ctx.set_source_rgb(*hilight_border_bottom)
                    ctx.fill()
                    ctx.save()
                    ctx.translate(0,offset_y+4+1)
                    ctx.rectangle(0,0,w,package_height-6)
                    ctx.set_source(gradient)
                    ctx.fill()
                    ctx.restore()
                    tr.font.font_color = 0xFFFFFFFF
                else:
                    ctx.rectangle(0,offset_y+4,w,package_height-4)
                    ctx.set_source_rgb(1,1,1)
                    ctx.fill()
                    tr.font.font_color = 0xFF000000
                if f.installed:
                    package_icon = get_icon(f.icon if f.icon else 'package',48,'package')
                else:
                    if f.hilight:
                        button.hilight = f.hilight
                    else:
                        button.hilight = 0
                    button.draw(self.buf,ctx,w-110,offset_y+11,100,32)
                    package_icon = get_icon('package-uninstalled',48)
                ctx.set_source_surface(package_icon,8,11+offset_y)
                ctx.paint()
                tr.set_richtext(f.text)
                tr.set_ellipsis()
                tr.set_max_lines(3)
                tr.draw(self.buf)
            offset_y += package_height

    def draw(self):
        surface = self.get_cairo_surface()

        WIDTH, HEIGHT = self.width - self.decorator.width(), self.height - self.decorator.height()

        ctx = cairo.Context(surface)
        ctx.translate(self.decorator.left_width(), self.decorator.top_height())
        ctx.rectangle(0,0,WIDTH,HEIGHT)
        ctx.set_source_rgb(1,1,1)
        ctx.fill()

        ctx.save()
        ctx.translate(0,self.menubar.height)
        text = self.buf.get_cairo_surface()
        ctx.set_source_surface(text,0,self.scroll_y)
        ctx.paint()
        ctx.restore()

        self.menubar.draw(ctx,0,0,WIDTH)

        self.decorator.render(self)
        self.flip()

    def finish_resize(self, msg):
        """Accept a resize."""
        if msg.width < 120 or msg.height < 120:
            self.resize_offer(max(msg.width,120),max(msg.height,120))
            return
        self.resize_accept(msg.width, msg.height)
        self.reinit()
        self.redraw_buf()
        self.draw()
        self.resize_done()
        self.flip()

    def scroll(self, amount):
        w,h = self.width - self.decorator.width(), self.height - self.decorator.height() - self.menubar.height
        self.scroll_y += amount
        if self.scroll_y > 0:
            self.scroll_y = 0
        max_scroll = self.buf.height - h if h < self.buf.height else 0
        if self.scroll_y < -max_scroll:
            self.scroll_y = -max_scroll

    def mouse_event(self, msg):
        if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE:
            window.close()
            sys.exit(0)
        x,y = msg.new_x - self.decorator.left_width(), msg.new_y - self.decorator.top_height()
        w,h = self.width - self.decorator.width(), self.height - self.decorator.height()

        if x >= 0 and x < w and y >= 0 and y < self.menubar.height:
            self.menubar.mouse_event(msg, x, y)
            return

        if x < 0 or x >= w or y < 0 or y >= h:
            return

        if x >= 0 and x < w and y >= self.menubar.height and y < h:
            if msg.buttons & yutani.MouseButton.SCROLL_UP:
                self.scroll(30)
                self.draw()
                return
            elif msg.buttons & yutani.MouseButton.SCROLL_DOWN:
                self.scroll(-30)
                self.draw()
                return

            if msg.buttons & yutani.MouseButton.BUTTON_RIGHT:
                if not self.menus:
                    pass # no context menu at the moment
                    #menu_entries = [
                    #    MenuEntryAction("Up",None,self.go_up,None),
                    #]
                    #menu = MenuWindow(menu_entries,(self.x+msg.new_x,self.y+msg.new_y),root=self)
                return

        if y < 0: return

        offset_y = self.scroll_y + self.menubar.height

        redraw = []
        hit = False

        for f in self.packages:
            if offset_y > h: break
            if y >= offset_y + 11 and y < offset_y + 11 + 32 and x >= w - 110 and x <= w - 10: # button height
                if not f.hilight:
                    redraw.append(f)
                    if self.hilighted:
                        redraw.append(self.hilighted)
                        self.hilighted.hilight = False
                    f.hilight = True
                self.hilighted = f
                hit = True
                break
            offset_y += package_height

        if not hit:
            if self.hilighted:
                redraw.append(self.hilighted)
                self.hilighted.hilight = False
                self.hilighted = None

        if self.hilighted:
            if msg.command == yutani.MouseEvent.DOWN:
                self.hilighted.hilight = 2
                redraw.append(self.hilighted)
            if msg.command == yutani.MouseEvent.RAISE or msg.command == yutani.MouseEvent.CLICK:
                if self.hilighted.do_action():
                    redraw = []
                    self.redraw_buf()
                    self.draw()

        if redraw:
            self.redraw_buf(redraw)
            self.draw()

    def keyboard_event(self, msg):
        if msg.event.action != yutani.KeyAction.ACTION_DOWN:
            return # Ignore anything that isn't a key down.
        if msg.event.key == b"q":
            self.close()
            sys.exit(0)
        elif msg.event.keycode == yutani.Keycode.PAGE_UP:
            h = self.height - self.decorator.height() - self.menubar.height
            self.scroll(int(h/2))
            self.draw()
        elif msg.event.keycode == yutani.Keycode.PAGE_DOWN:
            h = self.height - self.decorator.height() - self.menubar.height
            self.scroll(-int(h/2))
            self.draw()
        elif msg.event.keycode == yutani.Keycode.HOME:
            self.scroll_y = 0
            self.draw()
        elif msg.event.keycode == yutani.Keycode.END:
            h = self.height - self.decorator.height() - self.menubar.height
            self.scroll_y = -(self.buf.height - h if h < self.buf.height else 0)
            self.draw()
Esempio n. 16
0
    def __init__(self, decorator, path):
        super(PaintingWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="applications-painting", doublebuffer=True)
        self.move(100,100)
        self.x = 100
        self.y = 100
        self.decorator = decorator
        self.picker = None
        self.last_color = (0,0,0)
        self.modifiers = None
        self.checkpattern = self.checkerboard(24)

        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/applications-painting.png",_description,"applications-painting")

        def help_browser(action):
            subprocess.Popen(["help-browser.py","painting.trt"])

        def close_picker():
            self.last_color = self.picker.color
            self.picker = None

        def open_file(action):
            OpenFileDialog(self.decorator,"Open...",glob="*.png",callback=self.load_buffer,window=self)

        def new_surface(action):
            # TODO: prompt for size
            if self.buf:
                self.buf.destroy()
            self.new_buffer(*action)
            self.draw()

        def new_prompt(action):
            def input_callback(input_window):
                width = int(input_window.tr.text)
                input_window.close()
                def second_callback(input_window):
                    height = int(input_window.tr.text)
                    input_window.close()
                    new_surface((width,height))

                TextInputWindow(self.decorator,"Height?","new",text="500",callback=second_callback,window=self)
            TextInputWindow(self.decorator,"Width?","new",text="500",callback=input_callback,window=self)

        def save_file(action):
            self.modified = False
            path = '/tmp/painting.png'
            self.set_title(f'{os.path.basename(path)} - {app_name}',self.icon)
            self.surface.write_to_png(path)

        def select_color(action):
            if self.picker:
                return
            else:
                self.picker = ColorPickerWindow(self.decorator, close_picker)
                self.picker.draw()

        def clear_everything(action):
            self.draw_ctx.save()
            self.draw_ctx.set_operator(cairo.OPERATOR_SOURCE)
            self.draw_ctx.rectangle(0,0,self.surface.get_width(),self.surface.get_height())
            self.draw_ctx.set_source_rgba(0,0,0,0)
            self.draw_ctx.fill()
            self.draw_ctx.restore()

        menus = [
            ("File", [
                MenuEntrySubmenu("New...",[
                    MenuEntryAction("500×500","new",new_surface,(500,500)),
                    MenuEntryAction("800×600","new",new_surface,(800,600)),
                    MenuEntryAction("Custom...","new",new_prompt,None),
                ],icon="new"),
                MenuEntryAction("Open","open",open_file,None),
                MenuEntryAction("Save","save",save_file,None),
                MenuEntryDivider(),
                MenuEntryAction("Exit","exit",self.exit_app,None),
            ]),
            ("Tools", [
                MenuEntryAction("Color",None,select_color,None),
                MenuEntryAction("Clear Everything",None,clear_everything,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.menus = {}
        self.hovered_menu = None

        if not path:
            self.new_buffer(500,500)
        else:
            self.load_buffer(path)

        self.hilighted = None
        self.was_drawing = False
        self.line_width = 2.0
        self.curs_x = None
        self.curs_y = None
        self.moving = False
        self.scale = 1.0
        self.modified = False
Esempio n. 17
0
class PaintingWindow(yutani.Window):

    base_width = 600
    base_height = 600

    def __init__(self, decorator, path):
        super(PaintingWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="applications-painting", doublebuffer=True)
        self.move(100,100)
        self.x = 100
        self.y = 100
        self.decorator = decorator
        self.picker = None
        self.last_color = (0,0,0)
        self.modifiers = None
        self.checkpattern = self.checkerboard(24)

        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/applications-painting.png",_description,"applications-painting")

        def help_browser(action):
            subprocess.Popen(["help-browser.py","painting.trt"])

        def close_picker():
            self.last_color = self.picker.color
            self.picker = None

        def open_file(action):
            OpenFileDialog(self.decorator,"Open...",glob="*.png",callback=self.load_buffer,window=self)

        def new_surface(action):
            # TODO: prompt for size
            if self.buf:
                self.buf.destroy()
            self.new_buffer(*action)
            self.draw()

        def new_prompt(action):
            def input_callback(input_window):
                width = int(input_window.tr.text)
                input_window.close()
                def second_callback(input_window):
                    height = int(input_window.tr.text)
                    input_window.close()
                    new_surface((width,height))

                TextInputWindow(self.decorator,"Height?","new",text="500",callback=second_callback,window=self)
            TextInputWindow(self.decorator,"Width?","new",text="500",callback=input_callback,window=self)

        def save_file(action):
            self.modified = False
            path = '/tmp/painting.png'
            self.set_title(f'{os.path.basename(path)} - {app_name}',self.icon)
            self.surface.write_to_png(path)

        def select_color(action):
            if self.picker:
                return
            else:
                self.picker = ColorPickerWindow(self.decorator, close_picker)
                self.picker.draw()

        def clear_everything(action):
            self.draw_ctx.save()
            self.draw_ctx.set_operator(cairo.OPERATOR_SOURCE)
            self.draw_ctx.rectangle(0,0,self.surface.get_width(),self.surface.get_height())
            self.draw_ctx.set_source_rgba(0,0,0,0)
            self.draw_ctx.fill()
            self.draw_ctx.restore()

        menus = [
            ("File", [
                MenuEntrySubmenu("New...",[
                    MenuEntryAction("500×500","new",new_surface,(500,500)),
                    MenuEntryAction("800×600","new",new_surface,(800,600)),
                    MenuEntryAction("Custom...","new",new_prompt,None),
                ],icon="new"),
                MenuEntryAction("Open","open",open_file,None),
                MenuEntryAction("Save","save",save_file,None),
                MenuEntryDivider(),
                MenuEntryAction("Exit","exit",self.exit_app,None),
            ]),
            ("Tools", [
                MenuEntryAction("Color",None,select_color,None),
                MenuEntryAction("Clear Everything",None,clear_everything,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.menus = {}
        self.hovered_menu = None

        if not path:
            self.new_buffer(500,500)
        else:
            self.load_buffer(path)

        self.hilighted = None
        self.was_drawing = False
        self.line_width = 2.0
        self.curs_x = None
        self.curs_y = None
        self.moving = False
        self.scale = 1.0
        self.modified = False

    def actually_exit(self):
        menus = [x for x in self.menus.values()]
        for x in menus:
            x.definitely_close()
        self.close()
        sys.exit(0)

    def exit_app(self, action=None):
        if self.modified:
            DialogWindow(self.decorator,app_name,"You have unsaved changes.\nAre you sure you want to quit?",callback=self.actually_exit,window=self)
        else:
            self.actually_exit()


    def load_buffer(self,path):
        self.set_title(f'{os.path.basename(path)} - {app_name}',self.icon)
        s = cairo.ImageSurface.create_from_png(path)

        self.init_buffer(s.get_width(),s.get_height())
        self.draw_ctx.save()
        self.draw_ctx.set_operator(cairo.OPERATOR_SOURCE)
        self.draw_ctx.rectangle(0,0,self.surface.get_width(),self.surface.get_height())
        self.draw_ctx.set_source_rgba(0,0,0,0)
        self.draw_ctx.fill()
        self.draw_ctx.restore()
        self.draw_ctx.set_source_surface(s,0,0)
        self.draw_ctx.paint()

    def init_buffer(self,w,h):
        self.modified = False
        self.buf = yutani.GraphicsBuffer(w,h)
        self.surface = self.buf.get_cairo_surface()
        self.draw_ctx = cairo.Context(self.surface)
        self.offset_x = int((self.width-self.decorator.width()-self.buf.width)/2)
        self.offset_y = int((self.height-self.decorator.height()-self.buf.height-self.menubar.height)/2)

    def new_buffer(self,w,h):
        self.init_buffer(w,h)
        self.set_title(f'Untitled - {app_name}', self.icon)
        self.draw_ctx.rectangle(0,0,self.surface.get_width(),self.surface.get_height())
        self.draw_ctx.set_source_rgb(1,1,1)
        self.draw_ctx.fill()

    def color(self):
        if self.picker:
            return self.picker.color
        else:
            return self.last_color

    def checkerboard(self,size):
        s = cairo.ImageSurface(cairo.FORMAT_ARGB32,size,size)
        c = cairo.Context(s)
        c.set_source_rgb(128/255,128/255,128/255)
        c.paint()
        c.set_source_rgb(200/255,200/255,200/255)
        c.rectangle(size/2,0,size/2,size/2)
        c.rectangle(0,size/2,size/2,size/2)
        c.fill()
        return s

    def draw(self,decor=True,menu=True,clips=None):
        surface = self.get_cairo_surface()

        WIDTH, HEIGHT = self.width - self.decorator.width(), self.height - self.decorator.height()

        ctx = cairo.Context(surface)
        ctx.translate(self.decorator.left_width(), self.decorator.top_height())

        if clips:
            for clip in clips:
                x,y,a,b = clip
                width = a - x
                height = b - y
                ctx.rectangle(x,y,width,height)
            ctx.clip()

        ctx.save()
        ctx.set_source_surface(self.checkpattern,0,0)
        ctx.get_source().set_filter(cairo.FILTER_NEAREST)
        ctx.get_source().set_extend(cairo.EXTEND_REPEAT)
        ctx.rectangle(0,self.menubar.height,WIDTH,HEIGHT-self.menubar.height)
        ctx.fill()
        ctx.restore()

        ctx.save()
        ctx.translate(0,self.menubar.height)
        ctx.rectangle(0,0,WIDTH,HEIGHT-self.menubar.height)
        ctx.clip()


        ctx.save()
        ctx.scale(self.scale,self.scale)
        ctx.set_source_surface(self.surface,self.offset_x/self.scale,self.offset_y/self.scale)
        ctx.get_source().set_filter(cairo.FILTER_FAST)
        ctx.paint()

        if not self.curs_x is None:
            if self.scale < 1.0:
                ctx.set_line_width(0.5/self.scale)
            else:
                ctx.set_line_width(0.5)
            ctx.arc(self.curs_x/self.scale,self.curs_y/self.scale,self.line_width/2,0,2*math.pi)
            ctx.set_source_rgba(0,0,0,0.7)
            ctx.stroke()
            ctx.arc(self.curs_x/self.scale,self.curs_y/self.scale,self.line_width/2-0.5,0,2*math.pi)
            ctx.set_source_rgba(1,1,1,0.7)
            ctx.stroke()

        ctx.restore()
        ctx.restore()

        # For debugging clip regions.
        #if clips:
        #    for clip in clips:
        #        x,y,a,b = clip
        #        width = a - x
        #        height = b - y
        #        ctx.rectangle(x,y,width,height)
        #        ctx.set_source_rgba(1.0,0,0,0.4)
        #        ctx.paint()

        if menu:
            self.menubar.draw(ctx,0,0,WIDTH)

        if decor:
            self.decorator.render(self)

        self.flip()

    def finish_resize(self, msg):
        """Accept a resize."""
        if msg.width < 120 or msg.height < 120:
            self.resize_offer(max(msg.width,120),max(msg.height,120))
            return
        self.resize_accept(msg.width, msg.height)
        self.reinit()
        self.offset_x = int((self.width-self.decorator.width()-self.buf.width)/2)
        self.offset_y = int((self.height-self.decorator.height()-self.buf.height-self.menubar.height)/2)
        self.draw()
        self.resize_done()
        self.flip()

    def get_color(self,x,y):
        c = self.buf.get_value(x,y)
        a = (c >> 24) & 0xFF
        r = (c >> 16) & 0xFF
        g = (c >> 8)  & 0xFF
        b = (c) & 0xFF
        return (r,g,b)

    def mouse_event(self, msg):
        if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE:
            self.exit_app()
        x,y = msg.new_x - self.decorator.left_width(), msg.new_y - self.decorator.top_height()
        w,h = self.width - self.decorator.width(), self.height - self.decorator.height()

        if not self.was_drawing:
            if x >= 0 and x < w and y >= 0 and y < self.menubar.height:
                self.menubar.mouse_event(msg, x, y)
                return

            if x < 0 or x >= w or y < 0 or y >= h:
                return

            if x >= 0 and x < w and y >= self.menubar.height and y < h:
                if msg.buttons & yutani.MouseButton.BUTTON_RIGHT:
                    if self.picker:
                        _x,_y = x-self.offset_x,y-self.menubar.height-self.offset_y
                        if _x >= 0 and _x < self.surface.get_width() and _y >= 0 and _y < self.surface.get_height():
                            self.picker.set_color(*self.get_color(_x,_y))
                    if not self.menus:
                        pass # No context menu at the moment.
                        #menu_entries = [
                        #    MenuEntryAction("Up",None,self.go_up,None),
                        #]
                        #menu = MenuWindow(menu_entries,(self.x+msg.new_x,self.y+msg.new_y),root=self)
                    return

            if y < 0: return

        decor = False
        regions = []

        if not self.modifiers:
            if msg.buttons & yutani.MouseButton.SCROLL_UP:
                self.line_width *= 1.2
            elif msg.buttons & yutani.MouseButton.SCROLL_DOWN:
                self.line_width /= 1.2
        elif self.modifiers & yutani.Modifier.MOD_LEFT_CTRL:
            if msg.buttons & yutani.MouseButton.SCROLL_UP:
                self.scale += 0.1
                regions.append((0,0,w,h))
            elif msg.buttons & yutani.MouseButton.SCROLL_DOWN:
                self.scale -= 0.1
                if self.scale < 0.1:
                    self.scale = 0.1
                regions.append((0,0,w,h))

        if not (msg.buttons & yutani.MouseButton.BUTTON_LEFT):
            self.was_drawing = False

        if (msg.buttons & yutani.MouseButton.BUTTON_MIDDLE):
            if not self.moving:
                self.initial = msg.new_x, msg.new_y
                self.initial_off = self.offset_x, self.offset_y
                self.moving = True
            regions.append(
                (
                    self.offset_x,
                    self.offset_y+self.menubar.height,
                    self.offset_x+int(self.buf.width*self.scale),
                    self.offset_y+self.menubar.height+int(self.buf.height*self.scale)
                )
            )
            self.offset_x = self.initial_off[0] + msg.new_x - self.initial[0]
            self.offset_y = self.initial_off[1] + msg.new_y - self.initial[1]
            regions.append(
                (
                    self.offset_x,
                    self.offset_y+self.menubar.height,
                    self.offset_x+int(self.buf.width*self.scale),
                    self.offset_y+self.menubar.height+int(self.buf.height*self.scale)
                )
            )
        else:
            self.moving = False


        cur_x_new = msg.new_x - self.decorator.left_width()
        cur_y_new = msg.new_y - self.decorator.top_height()

        cur_x_old = msg.old_x - self.decorator.left_width()
        cur_y_old = msg.old_y - self.decorator.top_height()

        if (msg.command == yutani.MouseEvent.DRAG or msg.command == yutani.MouseEvent.DOWN) and msg.buttons & yutani.MouseButton.BUTTON_LEFT:
            self.was_drawing = True
            self.draw_ctx.set_line_cap(cairo.LINE_CAP_ROUND)
            self.draw_ctx.set_line_join(cairo.LINE_JOIN_ROUND)
            self.draw_ctx.set_source_rgb(*self.color())
            self.draw_ctx.set_line_width(self.line_width)

            x_1 = 0.5 + (cur_x_new - self.offset_x) / self.scale
            y_1 = 0.5 + (cur_y_new - self.offset_y - self.menubar.height) / self.scale
            if msg.command == yutani.MouseEvent.DOWN:
                x_0 = x_1
                y_0 = y_1
                regions.append(
                    (
                        int(cur_x_new - self.line_width * self.scale),
                        int(cur_y_new - self.line_width * self.scale),
                        int(cur_x_new + self.line_width * self.scale),
                        int(cur_y_new + self.line_width * self.scale),
                    )
                )
            else:
                x_0 = 0.5 + (cur_x_old - self.offset_x) / self.scale
                y_0 = 0.5 + (cur_y_old - self.offset_y - self.menubar.height) / self.scale
                regions.append(
                    (
                        int(min(cur_x_new,cur_x_old) - self.line_width * self.scale),
                        int(min(cur_y_new,cur_y_old) - self.line_width * self.scale),
                        int(max(cur_x_new,cur_x_old) + self.line_width * self.scale),
                        int(max(cur_y_new,cur_y_old) + self.line_width * self.scale),
                    )
                )

            self.draw_ctx.move_to(x_0,y_0)
            self.draw_ctx.line_to(x_1,y_1)
            self.draw_ctx.stroke()
            if not self.modified:
                self.modified = True
                self.set_title("*" + self.title, self.icon)
                decor = True
        else:
            regions.append(
                (
                    int(cur_x_new - self.line_width * self.scale),
                    int(cur_y_new - self.line_width * self.scale),
                    int(cur_x_new + self.line_width * self.scale),
                    int(cur_y_new + self.line_width * self.scale),
                )
            )


        if self.curs_x:
            regions.append(
                (
                    int(self.curs_x - self.line_width * self.scale - 1.0),
                    int(self.curs_y - self.line_width * self.scale + self.menubar.height - 1.0),
                    int(self.curs_x + self.line_width * self.scale + 1.0),
                    int(self.curs_y + self.line_width * self.scale + self.menubar.height + 1.0),
                )
            )
        self.curs_x = 0.5+msg.new_x - self.decorator.left_width()
        self.curs_y = 0.5+msg.new_y - self.decorator.top_height() - self.menubar.height
        self.draw(menu=False,decor=decor,clips=regions)

    def keyboard_event(self, msg):
        self.modifiers = msg.event.modifiers
        if msg.event.action != yutani.KeyAction.ACTION_DOWN:
            return # Ignore anything that isn't a key down.
        if msg.event.key == b"q":
            self.exit_app()
Esempio n. 18
0
    def __init__(self, decorator, path):
        super(FileBrowserWindow,
              self).__init__(self.base_width + decorator.width(),
                             self.base_height + decorator.height(),
                             title=app_name,
                             icon="folder",
                             doublebuffer=True)
        self.move(100, 100)
        self.x = 100
        self.y = 100
        self.decorator = decorator

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)

        def about_window(action):
            AboutAppletWindow(self.decorator, f"About {app_name}",
                              "/usr/share/icons/48/folder.png", _description,
                              "folder")

        def help_browser(action):
            subprocess.Popen(["help-browser.py", "file_browser.trt"])

        def input_path(action):
            def input_callback(input_window):
                text = input_window.tr.text
                input_window.close()
                self.load_directory(text)

            TextInputWindow(self.decorator,
                            "Open directory...",
                            "open",
                            text=self.path,
                            callback=input_callback,
                            window=self)

        menus = [
            ("File", [
                MenuEntryAction("Exit", "exit", exit_app, None),
            ]),
            ("Go", [
                MenuEntryAction("Path...", "open", input_path, None),
                MenuEntryDivider(),
                MenuEntryAction("Home", "home", self.load_directory,
                                os.environ.get("HOME")),
                MenuEntryAction("File System", None, self.load_directory, "/"),
                MenuEntryAction("Up", "up", self.go_up, None),
            ]),
            ("Help", [
                MenuEntryAction("Contents", "help", help_browser, None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}", "star", about_window,
                                None),
            ]),
        ]

        self.menubar = MenuBarWidget(self, menus)

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None

        self.buf = None
        self.load_directory(path)
        self.hilighted = None
Esempio n. 19
0
class HelpBrowserWindow(yutani.Window):

    base_width = 800
    base_height = 600

    def __init__(self, decorator):
        super(HelpBrowserWindow,
              self).__init__(self.base_width + decorator.width(),
                             self.base_height + decorator.height(),
                             title=app_name,
                             icon="help",
                             doublebuffer=True)
        self.move(100, 100)
        self.decorator = decorator
        self.current_topic = "0_index.trt"
        self.text_buffer = None
        self.text_offset = 0
        self.scroll_offset = 0
        self.tr = None
        self.size_changed = False

        self.special = {}
        self.special['contents'] = self.special_contents
        self.special['demo'] = self.special_demo
        self.down_text = None
        self.cache = {}
        self.history = []
        self.history_index = 0

        def herp(action):
            print(action)

        self.history_menu = MenuEntrySubmenu('History...',
                                             [MenuEntryDivider()])

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)

        def about_window(action):
            AboutAppletWindow(self.decorator, f"About {app_name}",
                              "/usr/share/icons/48/help.png", _description,
                              "help")

        menus = [
            (
                "File",
                [
                    #MenuEntryAction("Open...",None,print_derp,None),
                    #MenuEntryDivider(),
                    MenuEntryAction("Exit", "exit", exit_app, None),
                ]),
            ("Go", [
                MenuEntryAction("Home", "home", self.go_page, "0_index.trt"),
                MenuEntryAction("Topics", "bookmark", self.go_page,
                                "special:contents"),
                MenuEntryDivider(),
                self.history_menu,
                MenuEntryAction("Back", "back", self.go_back, None),
                MenuEntryAction("Forward", "forward", self.go_forward, None),
            ]),
            ("Help", [
                MenuEntryAction("Contents", "help", self.go_page,
                                "help_browser.trt"),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}", "star", about_window,
                                None),
            ]),
        ]

        self.menubar = MenuBarWidget(self, menus)

        self.menus = {}
        self.hovered_menu = None

        self.update_text_buffer()
        self.navigate("0_index.trt")

    def get_title(self, document):
        if document.startswith("special:"):
            if document[8:] in self.special:
                return self.special[document[8:]].__doc__
            return "???"
        elif document.startswith("http:"):
            if document in self.cache:
                lines = self.cache[document].split('\n')
                for x in lines:
                    x = x.strip()
                    if x.startswith('<h1>') and x.endswith('</h1>'):
                        return x[4:-5]
                return document.split('/')[-1].replace('.trt', '').title()
            else:
                return document
        elif document.startswith("file:"):
            path = document.replace("file:", "")
        else:
            path = f'/usr/share/help/{document}'
        if not os.path.exists(path):
            return "(file not found)"
        with open(path, 'r') as f:
            lines = f.readlines()
            for x in lines:
                x = x.strip()
                if x.startswith('<h1>') and x.endswith('</h1>'):
                    return x[4:-5]
            return document.replace('.trt', '').title()

    def special_contents(self):
        """Table of Contents"""
        # List all things.
        output = "\n<h1>Table of Contents</h1>\n\nThis table of contents is automatically generated.\n\n"
        output += "<h2>Special Pages</h2>\n\n"
        for k in self.special:
            output += f"➤ <link target=\"special:{k}\">{self.special[k].__doc__}</link>\n"
        output += "\n<h2>Documentation</h2>\n\n"
        for k in sorted(os.listdir('/usr/share/help')):
            if k.endswith('.trt'):
                output += f"➤ <link target=\"{k}\">{self.get_title(k)}</link>\n"
        for directory, _, files in os.walk('/usr/share/help'):
            if directory == '/usr/share/help':
                continue
            files = sorted([x for x in files if not x.startswith('.')])
            if files:
                d = directory.replace('/usr/share/help/', '')
                output += "\n<h3>" + d.title() + "</h3>\n\n"
                for k in files:
                    if k.endswith('.trt'):
                        k = d + '/' + k
                        output += f"➤ <link target=\"{k}\">{self.get_title(k)}</link>\n"
        return output

    def special_demo(self):
        """Formatting demo"""
        return f"""

<h1>This is a big header</h1>
This is text below that.
<h2>This is a medium header</h2>

<h3>This is a small header</h3>

This is normal text. <b>This is bold text.</b> <i>This is italic text.</i> <b><i>This is both.</i></b>
<link target=\"0_index.trt\">go home</link>"""

    def get_cache(self, url):
        if url in self.cache:
            return self.cache[url]
        else:
            try:
                text = subprocess.check_output(['fetch', url]).decode('utf-8')
            except:
                text = '\n<h1>Error</h1>\n\nThere was an error obtaining this file.'
            self.cache[url] = text
            return text

    def get_document_text(self):
        if self.current_topic.startswith("special:"):
            if self.current_topic[8:] in self.special:
                return self.special[self.current_topic[8:]]()
        elif self.current_topic.startswith("http:"):
            # Good luck
            return self.get_cache(self.current_topic)
        elif self.current_topic.startswith("file:"):
            path = self.current_topic.replace("file:", "")
        else:
            path = f'/usr/share/help/{self.current_topic}'
        if os.path.exists(path):
            with open(path, 'r') as f:
                return f.read()
        return f"""
<h1>Document Not Found</h1>

Uh oh, looks like the help document you tried to open ({self.current_topic}) wasn't available. Do you want to <link target=\"0_index.trt\">return to the index</link>?

You can also <link target=\"special:contents\">check the Table of Contents</link>.

"""

    def update_history(self):
        def go_history(action):
            self.navigate(self.history[action], touch_history=False)
            self.history_index = action
            self.update_history()

        entries = []
        for x in range(len(self.history)):
            t = self.get_title(self.history[x])
            e = MenuEntryAction(t, None, go_history, x)
            if x == self.history_index:
                e.title = f'<b>{t}</b>'
                e.rich = True
                e.update_text()
            entries.append(e)
        entries.reverse()
        self.history_menu.entries = entries

    def navigate(self, target, touch_history=True):
        if touch_history:
            del self.history[self.history_index + 1:]
            self.history.append(target)
            self.history_index = len(self.history) - 1
            self.update_history()
        self.current_topic = target
        self.text_offset = 0
        self.scroll_offset = 0
        self.tr.set_richtext(self.get_document_text())
        self.update_text_buffer()
        self.set_title(f"{self.get_title(self.current_topic)} - {app_name}",
                       "help")

    def update_text_buffer(self):
        if self.size_changed or not self.text_buffer:
            if self.text_buffer:
                self.text_buffer.destroy()
            self.text_buffer = yutani.GraphicsBuffer(
                self.width - self.decorator.width(), self.height -
                self.decorator.height() + 80 - self.menubar.height)
        surface = self.text_buffer.get_cairo_surface()
        ctx = cairo.Context(surface)
        ctx.rectangle(0, 0, surface.get_width(), surface.get_height())
        ctx.set_source_rgb(1, 1, 1)
        ctx.fill()

        pad = 10
        if not self.tr:
            self.tr = text_region.TextRegion(pad, 0,
                                             surface.get_width() - pad * 2,
                                             surface.get_height())
            self.tr.set_line_height(18)
            self.tr.base_dir = '/usr/share/help/'
            self.tr.set_richtext(self.get_document_text())
        elif self.size_changed:
            self.size_changed = False
            self.tr.resize(surface.get_width() - pad * 2,
                           surface.get_height() - pad * 2)

        self.tr.scroll = self.scroll_offset
        self.tr.draw(self.text_buffer)

    def draw(self):
        surface = self.get_cairo_surface()

        WIDTH, HEIGHT = self.width - self.decorator.width(
        ), self.height - self.decorator.height()

        ctx = cairo.Context(surface)
        ctx.translate(self.decorator.left_width(), self.decorator.top_height())
        ctx.rectangle(0, 0, WIDTH, HEIGHT)
        ctx.set_source_rgb(204 / 255, 204 / 255, 204 / 255)
        ctx.fill()

        ctx.save()
        ctx.translate(0, self.menubar.height)
        text = self.text_buffer.get_cairo_surface()
        ctx.set_source_surface(text, 0, -self.text_offset)
        ctx.paint()
        ctx.restore()

        self.menubar.draw(ctx, 0, 0, WIDTH)

        self.decorator.render(self)
        self.flip()

    def finish_resize(self, msg):
        """Accept a resize."""
        if msg.width < 100 or msg.height < 100:
            self.resize_offer(max(msg.width, 100), max(msg.height, 100))
            return

        self.resize_accept(msg.width, msg.height)
        self.reinit()
        self.size_changed = True
        self.update_text_buffer()
        self.draw()
        self.resize_done()
        self.flip()

    def scroll(self, amount):
        self.text_offset += amount
        while self.text_offset < 0:
            if self.scroll_offset == 0:
                self.text_offset = 0
            else:
                self.scroll_offset -= 1
                self.text_offset += self.tr.line_height
        while self.text_offset >= self.tr.line_height:
            self.scroll_offset += 1
            self.text_offset -= self.tr.line_height
        n = (len(self.tr.lines) - self.tr.visible_lines()) + 5
        n = n if n >= 0 else 0
        if self.scroll_offset >= n:
            self.scroll_offset = n
            self.text_offset = 0
        self.update_text_buffer()

    def text_under_cursor(self, msg):
        """Get the text unit under the cursor."""
        x = msg.new_x - self.decorator.left_width()
        y = msg.new_y - self.decorator.top_height(
        ) + self.text_offset - self.menubar.height
        return self.tr.click(x, y)

    def go_page(self, action):
        """Navigate to a page."""
        self.navigate(action)
        self.draw()

    def go_back(self, action):
        """Go back."""
        if self.history and self.history_index > 0:
            self.history_index -= 1
            self.navigate(self.history[self.history_index],
                          touch_history=False)
            self.update_history()
        self.draw()

    def go_forward(self, action):
        """Go forward."""
        if self.history and self.history_index < len(self.history) - 1:
            self.history_index += 1
            self.navigate(self.history[self.history_index],
                          touch_history=False)
            self.update_history()
        self.draw()

    def mouse_event(self, msg):
        if self.mouse_check(msg):
            self.draw()

    def mouse_check(self, msg):
        if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE:
            window.close()
            sys.exit(0)
        x, y = msg.new_x - self.decorator.left_width(
        ), msg.new_y - self.decorator.top_height()
        w, h = self.width - self.decorator.width(
        ), self.height - self.decorator.height()
        if x >= 0 and x < w and y >= 0 and y < self.menubar.height:
            self.menubar.mouse_event(msg, x, y)

        if x >= 0 and x < w and y >= self.menubar.height and y < h:
            if msg.buttons & yutani.MouseButton.BUTTON_RIGHT:
                if not self.menus:
                    menu_entries = [
                        MenuEntryAction("Back", "back", self.go_back, None),
                        MenuEntryAction("Forward", "forward", self.go_forward,
                                        None),
                    ]
                    menu = MenuWindow(menu_entries,
                                      (self.x + msg.new_x, self.y + msg.new_y),
                                      root=self)
        if msg.command == yutani.MouseEvent.DOWN:
            e = self.text_under_cursor(msg)
            r = False
            if self.down_text and e != self.down_text:
                for u in self.down_text.tag_group:
                    if u.unit_type == 4:
                        u.set_extra('hilight', False)
                    else:
                        u.set_font(self.down_font[u])
                del self.down_font
                self.down_text = None
                self.update_text_buffer()
                r = True
            if e and 'link' in e.extra and e.tag_group:
                self.down_font = {}
                for u in e.tag_group:
                    if u.unit_type == 4:
                        u.set_extra('hilight', True)
                    else:
                        new_font = toaru_fonts.Font(u.font.font_number,
                                                    u.font.font_size,
                                                    0xFFFF0000)
                        self.down_font[u] = u.font
                        u.set_font(new_font)
                self.update_text_buffer()
                r = True
                self.down_text = e
            else:
                self.down_text = None
            return r
        if msg.command == yutani.MouseEvent.CLICK or msg.command == yutani.MouseEvent.RAISE:
            e = self.text_under_cursor(msg)
            if self.down_text and e == self.down_text:
                self.navigate(e.extra['link'])
                return True
            elif self.down_text:
                for u in self.down_text.tag_group:
                    if u.unit_type == 4:
                        u.set_extra('hilight', False)
                    else:
                        u.set_font(self.down_font[u])
                del self.down_font
                self.down_text = None
                self.update_text_buffer()
                return True
        if msg.buttons & yutani.MouseButton.SCROLL_UP:
            self.scroll(-30)
            return True
        elif msg.buttons & yutani.MouseButton.SCROLL_DOWN:
            self.scroll(30)
            return True
        return False

    def keyboard_event(self, msg):
        if self.keyboard_check(msg):
            self.draw()

    def keyboard_check(self, msg):
        if msg.event.action != 0x01:
            return False  # Ignore anything that isn't a key down.
        if msg.event.keycode == yutani.Keycode.HOME:
            self.text_offset = 0
            self.scroll_offset = 0
            self.update_text_buffer()
            return True
        elif msg.event.keycode == yutani.Keycode.END:
            n = (len(self.tr.lines) - self.tr.visible_lines()) + 5
            self.scroll_offset = n if n >= 0 else 0
            self.text_offset = 0
            self.update_text_buffer()
            return True
        elif msg.event.keycode == yutani.Keycode.PAGE_UP:
            self.scroll(int(-self.height / 2))
            return True
        elif msg.event.keycode == yutani.Keycode.PAGE_DOWN:
            self.scroll(int(self.height / 2))
            return True
        elif msg.event.key == b"q":
            self.close()
            sys.exit(0)
Esempio n. 20
0
class HelpBrowserWindow(yutani.Window):

    base_width = 800
    base_height = 600

    def __init__(self, decorator):
        super(HelpBrowserWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="help", doublebuffer=True)
        self.move(100,100)
        self.x = 100
        self.y = 100
        self.decorator = decorator
        self.current_topic = "0_index.trt"
        self.text_buffer = None
        self.text_offset = 0
        self.tr = None
        self.size_changed = True
        self.text_scroller = ScrollableText()

        self.special = {}
        self.special['contents'] = self.special_contents
        self.special['demo'] = self.special_demo
        self.down_text = None
        self.cache = {}
        self.history = []
        self.history_index = 0
        self.title_cache = {}

        def herp(action):
            print(action)

        self.history_menu = MenuEntrySubmenu('History...',[MenuEntryDivider()])

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)

        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/help.png",_description,"help")

        menus = [
            ("File", [
                #MenuEntryAction("Open...",None,print_derp,None),
                #MenuEntryDivider(),
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Go", [
                MenuEntryAction("Home","home",self.go_page,"0_index.trt"),
                MenuEntryAction("Topics","bookmark",self.go_page,"special:contents"),
                MenuEntryDivider(),
                self.history_menu,
                MenuEntryAction("Back","back",self.go_back,None),
                MenuEntryAction("Forward","forward",self.go_forward,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",self.go_page,"help_browser.trt"),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.menus = {}
        self.hovered_menu = None

        self.update_text_buffer()
        self.navigate("0_index.trt")


    def get_title(self, document):
        if document.startswith("special:"):
            if document[8:] in self.special:
                return self.special[document[8:]].__doc__
            return "???"
        elif document.startswith("http:") or document.startswith('https:'):
            if document in self.title_cache:
                return self.title_cache[document]
            if document in self.cache:
                lines = self.cache[document].split('\n')
                for x in lines:
                    x = x.strip()
                    if x.startswith('<h1>') and x.endswith('</h1>'):
                        return x[4:-5]
                return document.split('/')[-1].replace('.trt','').title()
            else:
                return document
        elif document.startswith("file:"):
            path = document.replace("file:","")
        else:
            path = f'/usr/share/help/{document}'
        if not os.path.exists(path):
            return "(file not found)"
        with open(path,'r') as f:
            lines = f.readlines()
            for x in lines:
                x = x.strip()
                if x.startswith('<h1>') and x.endswith('</h1>'):
                    return x[4:-5]
            return document.replace('.trt','').title()

    def special_contents(self):
        """Table of Contents"""
        # List all things.
        output = "\n<h1>Table of Contents</h1>\n\nThis table of contents is automatically generated.\n\n"
        output += "<h2>Special Pages</h2>\n\n"
        for k in self.special:
            output += f"➤ <link target=\"special:{k}\">{self.special[k].__doc__}</link>\n"
        output += "\n<h2>Documentation</h2>\n\n"
        for k in sorted(os.listdir('/usr/share/help')):
            if k.endswith('.trt'):
                output += f"➤ <link target=\"{k}\">{self.get_title(k)}</link>\n"
        for directory,_,files in os.walk('/usr/share/help'):
            if directory == '/usr/share/help':
                continue
            files = sorted([x for x in files if not x.startswith('.')])
            if files:
                d = directory.replace('/usr/share/help/','')
                output += "\n<h3>" + d.title() + "</h3>\n\n"
                for k in files:
                    if k.endswith('.trt'):
                        k = d + '/' + k
                        output += f"➤ <link target=\"{k}\">{self.get_title(k)}</link>\n"
        return output

    def special_demo(self):
        """Formatting demo"""
        return f"""

<h1>This is a big header</h1>
This is text below that.
<h2>This is a medium header</h2>

<h3>This is a small header</h3>

This is normal text. <b>This is bold text.</b> <i>This is italic text.</i> <b><i>This is both.</i></b>
<link target=\"0_index.trt\">go home</link>"""

    def get_cache(self, url):
        if url in self.cache:
            return self.cache[url]
        else:
            try:
                text = subprocess.check_output(['fetch',url])
                if text.startswith(b'\x89PNG'):
                    text = f"<html><body><img src=\"{url}\"></body></html>"
                else:
                    text = text.decode('utf-8')
            except:
                text = '\n<h1>Error</h1>\n\nThere was an error obtaining this file.'
            self.cache[url] = text
            return text

    def get_document_text(self):
        if self.current_topic.startswith("special:"):
            if self.current_topic[8:] in self.special:
                return self.special[self.current_topic[8:]]()
        elif self.current_topic.startswith("http:") or self.current_topic.startswith('https:'):
            # Good luck
            return self.get_cache(self.current_topic)
        elif self.current_topic.startswith("file:"):
            path = self.current_topic.replace("file:","")
        else:
            path = f'/usr/share/help/{self.current_topic}'
        if os.path.exists(path):
            with open(path,'r') as f:
                return f.read()
        return f"""
<h1>Document Not Found</h1>

Uh oh, looks like the help document you tried to open ({self.current_topic}) wasn't available. Do you want to <link target=\"0_index.trt\">return to the index</link>?

You can also <link target=\"special:contents\">check the Table of Contents</link>.

"""

    def is_html(self):
        if self.current_topic.endswith('.html') or self.current_topic.endswith('.htm'): return True
        if self.current_topic.startswith('http') and not self.current_topic.endswith('.trt'): return True
        if '<html' in self.get_document_text(): return True
        return False

    def update_history(self):
        def go_history(action):
            self.navigate(self.history[action],touch_history=False)
            self.history_index = action
            self.update_history()
        entries = []
        for x in range(len(self.history)):
            t = self.get_title(self.history[x])
            e = MenuEntryAction(t,None,go_history,x)
            if x == self.history_index:
                e.title = f'<b>{t}</b>'
                e.rich = True
                e.update_text()
            entries.append(e)
        entries.reverse()
        self.history_menu.entries = entries

    def navigate(self, target, touch_history=True):
        #if target.startswith('https:'):
        #    DialogWindow(self.decorator,app_name,f"<mono>https</mono> is not supported. Could not load the URL <mono>{target}</mono>",callback=lambda: None,window=self,cancel_label=False)
        #    return
        if touch_history:
            del self.history[self.history_index+1:]
            self.history.append(target)
            self.history_index = len(self.history)-1
        self.current_topic = target
        self.text_offset = 0
        if self.is_html():
            self.tr.base_dir = os.path.dirname(target) + '/'
        else:
            self.tr.base_dir = '/usr/share/help/'
        self.tr.set_richtext(self.get_document_text(),html=self.is_html())
        self.update_text_buffer()
        if self.tr.title:
            self.set_title(f"{self.tr.title} - {app_name}","help")
            self.title_cache[target] = self.tr.title
        else:
            self.set_title(f"{self.get_title(self.current_topic)} - {app_name}","help")
        self.update_history()

    def update_text_buffer(self):
        if not self.tr:
            self.tr = text_region.TextRegion(0,0,100,100)
            self.tr.set_line_height(18)
            self.tr.base_dir = '/usr/share/help/'
            self.tr.set_richtext(self.get_document_text(),html=self.is_html())
            self.text_scroller.tr = self.tr

        if self.size_changed:
            self.text_scroller.update(self.width - self.decorator.width())

        #self.tr.scroll = self.scroll_offset
        #self.tr.draw(self.text_buffer)

    def draw(self):
        surface = self.get_cairo_surface()

        WIDTH, HEIGHT = self.width - self.decorator.width(), self.height - self.decorator.height()

        ctx = cairo.Context(surface)
        ctx.translate(self.decorator.left_width(), self.decorator.top_height())
        ctx.rectangle(0,0,WIDTH,HEIGHT)
        #ctx.set_source_rgb(204/255,204/255,204/255)
        ctx.set_source_rgb(1,1,1)
        ctx.fill()

        ctx.save()
        ctx.translate(0,self.menubar.height)
        """
        text = self.text_buffer.get_cairo_surface()
        ctx.set_source_surface(text,0,-self.text_offset)
        ctx.paint()
        """
        self.text_scroller.draw(ctx,0,0,HEIGHT-self.menubar.height,self.text_offset)
        ctx.restore()

        self.menubar.draw(ctx,0,0,WIDTH)

        self.decorator.render(self)
        self.flip()

    def finish_resize(self, msg):
        """Accept a resize."""
        if msg.width < 100 or msg.height < 100:
            self.resize_offer(max(msg.width,100),max(msg.height,100))
            return

        self.resize_accept(msg.width, msg.height)
        self.reinit()
        self.size_changed = True
        self.update_text_buffer()
        self.draw()
        self.resize_done()
        self.flip()

    def scroll(self, amount):
        self.text_offset += amount
        if self.text_offset < 0:
            self.text_offset = 0
        if self.text_offset > self.text_scroller.scroll_max():
            self.text_offset = self.text_scroller.scroll_max()

    def text_under_cursor(self, msg):
        """Get the text unit under the cursor."""
        x = msg.new_x - self.decorator.left_width()
        y = msg.new_y - self.decorator.top_height() + self.text_offset - self.menubar.height
        return self.tr.click(x,y)


    def go_page(self, action):
        """Navigate to a page."""
        self.navigate(action)
        self.draw()

    def go_back(self,action):
        """Go back."""
        if self.history and self.history_index > 0:
            self.history_index -= 1
            self.navigate(self.history[self.history_index], touch_history=False)
            self.update_history()
        self.draw()

    def go_forward(self,action):
        """Go forward."""
        if self.history and self.history_index < len(self.history)-1:
            self.history_index += 1
            self.navigate(self.history[self.history_index], touch_history=False)
            self.update_history()
        self.draw()

    def mouse_event(self, msg):
        if self.mouse_check(msg):
            self.draw()

    def mouse_check(self, msg):
        if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE:
            window.close()
            sys.exit(0)
        x,y = msg.new_x - self.decorator.left_width(), msg.new_y - self.decorator.top_height()
        w,h = self.width - self.decorator.width(), self.height - self.decorator.height()
        if x >= 0 and x < w and y >= 0 and y < self.menubar.height:
            self.menubar.mouse_event(msg, x, y)

        if x >= 0 and x < w and y >= self.menubar.height and y < h:
            if msg.buttons & yutani.MouseButton.BUTTON_RIGHT:
                if not self.menus:
                    menu_entries = [
                        MenuEntryAction("Back","back",self.go_back,None),
                        MenuEntryAction("Forward","forward",self.go_forward,None),
                    ]
                    menu = MenuWindow(menu_entries,(self.x+msg.new_x,self.y+msg.new_y),root=self)
            if msg.buttons & yutani.MouseButton.SCROLL_UP:
                self.scroll(-30)
                return True
            elif msg.buttons & yutani.MouseButton.SCROLL_DOWN:
                self.scroll(30)
                return True
            if msg.command == yutani.MouseEvent.DOWN:
                e = self.text_under_cursor(msg)
                r = False
                if self.down_text and e != self.down_text:
                    for u in self.down_text.tag_group:
                        if u.unit_type == 4:
                            u.set_extra('hilight',False)
                        else:
                            u.set_font(self.down_font[u])
                    del self.down_font
                    self.down_text = None
                    self.update_text_buffer()
                    r = True
                if e and 'link' in e.extra and e.tag_group:
                    self.down_font = {}
                    for u in e.tag_group:
                        if u.unit_type == 4:
                            u.set_extra('hilight',True)
                        else:
                            new_font = toaru_fonts.Font(u.font.font_number,u.font.font_size,0xFFFF0000)
                            self.down_font[u] = u.font
                            u.set_font(new_font)
                    self.update_text_buffer()
                    r = True
                    self.down_text = e
                else:
                    self.down_text = None
                return r
            if msg.command == yutani.MouseEvent.CLICK or msg.command == yutani.MouseEvent.RAISE:
                e = self.text_under_cursor(msg)
                if self.down_text and e == self.down_text:
                    self.navigate(e.extra['link'])
                    return True
                elif self.down_text:
                    for u in self.down_text.tag_group:
                        if u.unit_type == 4:
                            u.set_extra('hilight',False)
                        else:
                            u.set_font(self.down_font[u])
                    del self.down_font
                    self.down_text = None
                    self.update_text_buffer()
                    return True

        return False

    def keyboard_event(self, msg):
        if self.keyboard_check(msg):
            self.draw()

    def keyboard_check(self,msg):
        if msg.event.action != 0x01:
            return False # Ignore anything that isn't a key down.
        if msg.event.keycode == yutani.Keycode.HOME:
            self.text_offset = 0
            return True
        elif msg.event.keycode == yutani.Keycode.END:
            n = (len(self.tr.lines)-self.tr.visible_lines())+5
            self.text_offset = self.text_scroller.scroll_max()
            return True
        elif msg.event.keycode == yutani.Keycode.PAGE_UP:
            self.scroll(int(-self.height/2))
            return True
        elif msg.event.keycode == yutani.Keycode.PAGE_DOWN:
            self.scroll(int(self.height/2))
            return True
        elif msg.event.key == b"q":
            self.close()
            sys.exit(0)
Esempio n. 21
0
    def __init__(self, decorator):
        super(HelpBrowserWindow,
              self).__init__(self.base_width + decorator.width(),
                             self.base_height + decorator.height(),
                             title=app_name,
                             icon="help",
                             doublebuffer=True)
        self.move(100, 100)
        self.decorator = decorator
        self.current_topic = "0_index.trt"
        self.text_buffer = None
        self.text_offset = 0
        self.scroll_offset = 0
        self.tr = None
        self.size_changed = False

        self.special = {}
        self.special['contents'] = self.special_contents
        self.special['demo'] = self.special_demo
        self.down_text = None
        self.cache = {}
        self.history = []
        self.history_index = 0

        def herp(action):
            print(action)

        self.history_menu = MenuEntrySubmenu('History...',
                                             [MenuEntryDivider()])

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)

        def about_window(action):
            AboutAppletWindow(self.decorator, f"About {app_name}",
                              "/usr/share/icons/48/help.png", _description,
                              "help")

        menus = [
            (
                "File",
                [
                    #MenuEntryAction("Open...",None,print_derp,None),
                    #MenuEntryDivider(),
                    MenuEntryAction("Exit", "exit", exit_app, None),
                ]),
            ("Go", [
                MenuEntryAction("Home", "home", self.go_page, "0_index.trt"),
                MenuEntryAction("Topics", "bookmark", self.go_page,
                                "special:contents"),
                MenuEntryDivider(),
                self.history_menu,
                MenuEntryAction("Back", "back", self.go_back, None),
                MenuEntryAction("Forward", "forward", self.go_forward, None),
            ]),
            ("Help", [
                MenuEntryAction("Contents", "help", self.go_page,
                                "help_browser.trt"),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}", "star", about_window,
                                None),
            ]),
        ]

        self.menubar = MenuBarWidget(self, menus)

        self.menus = {}
        self.hovered_menu = None

        self.update_text_buffer()
        self.navigate("0_index.trt")
Esempio n. 22
0
    def __init__(self, decorator):
        super(MinesWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="mines", doublebuffer=True)
        self.move(100,100)
        self.decorator = decorator
        self.button_width = {}
        self.button_height = 0

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)
        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/mines.png",_description,"mines")
        def help_browser(action):
            subprocess.Popen(["help-browser.py","mines.trt"])
        def custom_game(action):
            def input_callback(input_window):
                size = int(input_window.tr.text)
                input_window.close()
                def second_callback(input_window):
                    mines = int(input_window.tr.text)
                    input_window.close()
                    self.new_game((size,mines))

                TextInputWindow(self.decorator,"How many mines?","mines",text="90",callback=second_callback,window=self)
            TextInputWindow(self.decorator,"How wide/tall?","mines",text="20",callback=input_callback,window=self)

        menus = [
            ("File", [
                MenuEntrySubmenu("New Game...",[
                    MenuEntryAction("9×9, 10 mines",None,self.new_game,(9,10)),
                    MenuEntryAction("16×16, 40 mines",None,self.new_game,(16,40)),
                    MenuEntryAction("20×20, 90 mines",None,self.new_game,(20,90)),
                    MenuEntryAction("Custom...",None,custom_game,None),
                ],icon="new"),
                MenuEntryDivider(),
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.tr = text_region.TextRegion(self.decorator.left_width()+5,self.decorator.top_height()+self.menubar.height,self.base_width-10,40)
        self.tr.set_font(toaru_fonts.Font(toaru_fonts.FONT_SANS_SERIF,18))
        self.tr.set_alignment(2)
        self.tr.set_valignment(2)
        self.tr.set_one_line()
        self.tr.set_ellipsis()


        self.error = False

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None
        self.modifiers = 0

        self.new_game((9,10))
Esempio n. 23
0
class FileBrowserWindow(yutani.Window):

    base_width = 400
    base_height = 300

    def __init__(self, decorator, path):
        super(FileBrowserWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="folder", doublebuffer=True)
        self.move(100,100)
        self.x = 100
        self.y = 100
        self.decorator = decorator

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)
        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/folder.png",_description,"folder")
        def help_browser(action):
            subprocess.Popen(["help-browser.py","file_browser.trt"])

        def input_path(action):
            def input_callback(input_window):
                text = input_window.tr.text
                input_window.close()
                self.load_directory(text)
            TextInputWindow(self.decorator,"Open directory...","open",text=self.path,callback=input_callback,window=self)

        menus = [
            ("File", [
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Go", [
                MenuEntryAction("Path...","open",input_path,None),
                MenuEntryDivider(),
                MenuEntryAction("Home","home",self.load_directory,os.environ.get("HOME")),
                MenuEntryAction("File System",None,self.load_directory,"/"),
                MenuEntryAction("Up","up",self.go_up,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None

        self.buf = None
        self.load_directory(path)
        self.hilighted = None

    def go_up(self, action):
        self.load_directory(os.path.abspath(os.path.join(self.path,'..')))
        self.draw()

    def load_directory(self, path):
        if not os.path.exists(path):
            DialogWindow(self.decorator,app_name,f"The path <mono>{path}</mono> could not be opened. (Not found)",window=self,icon='folder')
            return
        if not os.path.isdir(path):
            DialogWindow(self.decorator,app_name,f"The path <mono>{path}</mono> could not be opened. (Not a directory)",window=self,icon='folder')
            return
        path = os.path.normpath(path)
        self.path = path
        title = "/" if path == "/" else os.path.basename(path)
        self.set_title(f"{title} - {app_name}",'folder')

        self.files = sorted([File(os.path.join(path,f), self) for f in os.listdir(path)], key=lambda x: x.sortkey)
        self.scroll_y = 0
        self.hilighted = None
        self.redraw_buf()

    def redraw_buf(self,icons=None):
        if self.buf:
            self.buf.destroy()
        w = self.width - self.decorator.width()
        files_per_row = int(w / 100)
        self.buf = yutani.GraphicsBuffer(w,math.ceil(len(self.files)/files_per_row)*100)

        surface = self.buf.get_cairo_surface()
        ctx = cairo.Context(surface)

        if icons:
            for icon in icons:
                ctx.rectangle(icon.x,icon.y,100,100)
            ctx.clip()


        ctx.rectangle(0,0,surface.get_width(),surface.get_height())
        ctx.set_source_rgb(1,1,1)
        ctx.fill()

        offset_x = 0
        offset_y = 0

        for f in self.files:
            if not icons or f in icons:
                x_, y_ = ctx.user_to_device(0,0)
                f.tr.move(offset_x,offset_y+60)
                f.tr.draw(self.buf)
                ctx.set_source_surface(f.icon,offset_x + 26,offset_y+10)
                ctx.paint_with_alpha(1.0 if not f.hilight else 0.7)
            f.x = offset_x
            f.y = offset_y
            offset_x += 100
            if offset_x + 100 > surface.get_width():
                offset_x = 0
                offset_y += 100

    def draw(self):
        surface = self.get_cairo_surface()

        WIDTH, HEIGHT = self.width - self.decorator.width(), self.height - self.decorator.height()

        ctx = cairo.Context(surface)
        ctx.translate(self.decorator.left_width(), self.decorator.top_height())
        ctx.rectangle(0,0,WIDTH,HEIGHT)
        ctx.set_source_rgb(1,1,1)
        ctx.fill()

        ctx.save()
        ctx.translate(0,self.menubar.height)
        text = self.buf.get_cairo_surface()
        ctx.set_source_surface(text,0,self.scroll_y)
        ctx.paint()
        ctx.restore()

        self.menubar.draw(ctx,0,0,WIDTH)

        self.decorator.render(self)
        self.flip()

    def finish_resize(self, msg):
        """Accept a resize."""
        if msg.width < 120 or msg.height < 120:
            self.resize_offer(max(msg.width,120),max(msg.height,120))
            return
        self.resize_accept(msg.width, msg.height)
        self.reinit()
        self.redraw_buf()
        self.draw()
        self.resize_done()
        self.flip()

    def scroll(self, amount):
        w,h = self.width - self.decorator.width(), self.height - self.decorator.height()
        files_per_row = int(w / 100)
        rows_total = math.ceil(len(self.files) / files_per_row)
        rows_visible = int((h - 24) / 100)
        rows = rows_total - rows_visible
        if rows < 0: rows = 0
        self.scroll_y += amount
        if self.scroll_y > 0:
            self.scroll_y = 0
        if self.scroll_y < -100 * rows:
            self.scroll_y = -100 * rows

    def mouse_event(self, msg):
        if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE:
            window.close()
            sys.exit(0)
        x,y = msg.new_x - self.decorator.left_width(), msg.new_y - self.decorator.top_height()
        w,h = self.width - self.decorator.width(), self.height - self.decorator.height()

        if x >= 0 and x < w and y >= 0 and y < self.menubar.height:
            self.menubar.mouse_event(msg, x, y)
            return

        if x >= 0 and x < w and y >= self.menubar.height and y < h:
            if msg.buttons & yutani.MouseButton.SCROLL_UP:
                self.scroll(30)
                self.draw()
                return
            elif msg.buttons & yutani.MouseButton.SCROLL_DOWN:
                self.scroll(-30)
                self.draw()
                return

            if msg.buttons & yutani.MouseButton.BUTTON_RIGHT:
                if not self.menus:
                    menu_entries = [
                        MenuEntryAction("Up","up",self.go_up,None),
                    ]
                    menu = MenuWindow(menu_entries,(self.x+msg.new_x,self.y+msg.new_y),root=self)
                return

        if y < 0: return

        offset_x = 0
        offset_y = self.scroll_y + self.menubar.height

        redraw = []

        files_per_row = int(w / 100)
        rows_total = math.ceil(len(self.files) / files_per_row)
        skip_files = files_per_row * (int(-offset_y / 100))
        offset_y += int(-offset_y/100) * 100

        hit = False
        for f in self.files[skip_files:]:
            if offset_y > h: break
            if offset_y > -100:
                if x >= offset_x and x < offset_x + 100 and y >= offset_y and y < offset_y + 100:
                    if not f.hilight:
                        redraw.append(f)
                        if self.hilighted:
                            redraw.append(self.hilighted)
                            self.hilighted.hilight = False
                        f.hilight = True
                    self.hilighted = f
                    hit = True
                    break
            offset_x += 100
            if offset_x + 100 > w:
                offset_x = 0
                offset_y += 100
        if not hit:
            if self.hilighted:
                redraw.append(self.hilighted)
                self.hilighted.hilight = False
                self.hilighted = None

        if self.hilighted:
            if msg.command == yutani.MouseEvent.DOWN:
                self.hilighted.do_action()

        if redraw:
            self.redraw_buf(redraw)
            self.draw()

    def keyboard_event(self, msg):
        if msg.event.action != yutani.KeyAction.ACTION_DOWN:
            return # Ignore anything that isn't a key down.
        if msg.event.key == b"q":
            self.close()
            sys.exit(0)
Esempio n. 24
0
    def __init__(self, decorator):
        super(HelpBrowserWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="help", doublebuffer=True)
        self.move(100,100)
        self.x = 100
        self.y = 100
        self.decorator = decorator
        self.current_topic = "0_index.trt"
        self.text_buffer = None
        self.text_offset = 0
        self.tr = None
        self.size_changed = True
        self.text_scroller = ScrollableText()

        self.special = {}
        self.special['contents'] = self.special_contents
        self.special['demo'] = self.special_demo
        self.down_text = None
        self.cache = {}
        self.history = []
        self.history_index = 0
        self.title_cache = {}

        def herp(action):
            print(action)

        self.history_menu = MenuEntrySubmenu('History...',[MenuEntryDivider()])

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)

        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/48/help.png",_description,"help")

        menus = [
            ("File", [
                #MenuEntryAction("Open...",None,print_derp,None),
                #MenuEntryDivider(),
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Go", [
                MenuEntryAction("Home","home",self.go_page,"0_index.trt"),
                MenuEntryAction("Topics","bookmark",self.go_page,"special:contents"),
                MenuEntryDivider(),
                self.history_menu,
                MenuEntryAction("Back","back",self.go_back,None),
                MenuEntryAction("Forward","forward",self.go_forward,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",self.go_page,"help_browser.trt"),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.menus = {}
        self.hovered_menu = None

        self.update_text_buffer()
        self.navigate("0_index.trt")
Esempio n. 25
0
    def __init__(self, decorator, path):
        super(PaintingWindow,
              self).__init__(self.base_width + decorator.width(),
                             self.base_height + decorator.height(),
                             title=app_name,
                             icon="applications-painting",
                             doublebuffer=True)
        self.move(100, 100)
        self.x = 100
        self.y = 100
        self.decorator = decorator
        self.picker = None
        self.last_color = (0, 0, 0)
        self.modifiers = None
        self.checkpattern = self.checkerboard(24)

        def about_window(action):
            AboutAppletWindow(self.decorator, f"About {app_name}",
                              "/usr/share/icons/48/applications-painting.png",
                              _description, "applications-painting")

        def help_browser(action):
            subprocess.Popen(["help-browser.py", "painting.trt"])

        def close_picker():
            self.last_color = self.picker.color
            self.picker = None

        def open_file(action):
            OpenFileDialog(self.decorator,
                           "Open...",
                           glob="*.png",
                           callback=self.load_buffer,
                           window=self)

        def new_surface(action):
            # TODO: prompt for size
            if self.buf:
                self.buf.destroy()
            self.new_buffer(*action)
            self.draw()

        def new_prompt(action):
            def input_callback(input_window):
                width = int(input_window.tr.text)
                input_window.close()

                def second_callback(input_window):
                    height = int(input_window.tr.text)
                    input_window.close()
                    new_surface((width, height))

                TextInputWindow(self.decorator,
                                "Height?",
                                "new",
                                text="500",
                                callback=second_callback,
                                window=self)

            TextInputWindow(self.decorator,
                            "Width?",
                            "new",
                            text="500",
                            callback=input_callback,
                            window=self)

        def save_file(action):
            self.modified = False
            path = '/tmp/painting.png'
            self.set_title(f'{os.path.basename(path)} - {app_name}', self.icon)
            self.surface.write_to_png(path)

        def select_color(action):
            if self.picker:
                return
            else:
                self.picker = ColorPickerWindow(self.decorator, close_picker)
                self.picker.draw()

        def clear_everything(action):
            self.draw_ctx.save()
            self.draw_ctx.set_operator(cairo.OPERATOR_SOURCE)
            self.draw_ctx.rectangle(0, 0, self.surface.get_width(),
                                    self.surface.get_height())
            self.draw_ctx.set_source_rgba(0, 0, 0, 0)
            self.draw_ctx.fill()
            self.draw_ctx.restore()

        menus = [
            ("File", [
                MenuEntrySubmenu("New...", [
                    MenuEntryAction("500×500", "new", new_surface, (500, 500)),
                    MenuEntryAction("800×600", "new", new_surface, (800, 600)),
                    MenuEntryAction("Custom...", "new", new_prompt, None),
                ],
                                 icon="new"),
                MenuEntryAction("Open", "open", open_file, None),
                MenuEntryAction("Save", "save", save_file, None),
                MenuEntryDivider(),
                MenuEntryAction("Exit", "exit", self.exit_app, None),
            ]),
            ("Tools", [
                MenuEntryAction("Color", None, select_color, None),
                MenuEntryAction("Clear Everything", None, clear_everything,
                                None),
            ]),
            ("Help", [
                MenuEntryAction("Contents", "help", help_browser, None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}", "star", about_window,
                                None),
            ]),
        ]

        self.menubar = MenuBarWidget(self, menus)

        self.menus = {}
        self.hovered_menu = None

        if not path:
            self.new_buffer(500, 500)
        else:
            self.load_buffer(path)

        self.hilighted = None
        self.was_drawing = False
        self.line_width = 2.0
        self.curs_x = None
        self.curs_y = None
        self.moving = False
        self.scale = 1.0
        self.modified = False
Esempio n. 26
0
class PDFViewerWindow(yutani.Window):

    base_width = 640
    base_height = 480

    def __init__(self, decorator, path):
        super(PDFViewerWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title=app_name, icon="pdfviewer", doublebuffer=True)
        self.move(100,100)
        self.x = 100
        self.y = 100
        self.decorator = decorator

        self.fitz_lib = ctypes.CDLL('libtoaru-fitz.so')
        self.fitz_lib.init_fitz.restype = ctypes.c_void_p
        self.fitz_lib.load_document.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
        self.fitz_lib.load_document.restype = ctypes.c_void_p
        self.fitz_lib.set_fit.argtypes = [ctypes.c_int]
        self.fitz_lib.draw_page.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p]
        self.fitz_lib.page_count.argtypes = [ctypes.c_void_p]
        self.fitz_lib.page_count.restype = ctypes.c_int

        def open_file(action):
            OpenFileDialog(self.decorator,"Open PDF...",glob="*.pdf",callback=self.load_file,window=self)

        def exit_app(action):
            menus = [x for x in self.menus.values()]
            for x in menus:
                x.definitely_close()
            self.close()
            sys.exit(0)
        def about_window(action):
            AboutAppletWindow(self.decorator,f"About {app_name}","/usr/share/icons/external/pdfviewer.png",_description,"pdfviewer")
        def help_browser(action):
            subprocess.Popen(["help-browser.py","downloaded/mupdf.trt"])
        menus = [
            ("File", [
                MenuEntryAction("Open","open",open_file,None),
                MenuEntryDivider(),
                MenuEntryAction("Exit","exit",exit_app,None),
            ]),
            ("Help", [
                MenuEntryAction("Contents","help",help_browser,None),
                MenuEntryDivider(),
                MenuEntryAction(f"About {app_name}","star",about_window,None),
            ]),
        ]

        self.menubar = MenuBarWidget(self,menus)

        self.hover_widget = None
        self.down_button = None

        self.menus = {}
        self.hovered_menu = None

        self.buf = None
        self.load_file(path)
        self.hilighted = None

    def load_file(self, path):
        if not path or not os.path.exists(path):
            self.document = None
            self.redraw_buf(True)
            self.page = 1
            self.page_count = 1
        else:
            self.fitz_ctx = self.fitz_lib.init_fitz()
            self.path = path
            self.document = self.fitz_lib.load_document(self.fitz_ctx, path.encode('utf-8'))
            self.page = 1
            self.page_count = self.fitz_lib.page_count(self.document)
            self.redraw_buf(True)

    def go_up(self, action):
        pass

    def redraw_buf(self,size_changed=False):
        if size_changed:
            if self.buf:
                self.buf.destroy()
            w = self.width - self.decorator.width()
            h = self.height - self.decorator.height()
            self.buf = yutani.GraphicsBuffer(w,h)
        yutani.Window.fill(self.buf,0xFF777777)
        if self.document:
            self.fitz_lib.draw_page(self.fitz_ctx, self.document, self.page, self.buf._gfx)
            self.set_title(f"{self.path} - {self.page}/{self.page_count} - {app_name}","pdfviewer")
        else:
            self.set_title(app_name,"pdfviewer")


    def draw(self):
        surface = self.get_cairo_surface()

        WIDTH, HEIGHT = self.width - self.decorator.width(), self.height - self.decorator.height()

        ctx = cairo.Context(surface)
        ctx.translate(self.decorator.left_width(), self.decorator.top_height())
        ctx.rectangle(0,0,WIDTH,HEIGHT)
        ctx.set_source_rgb(1,1,1)
        ctx.fill()

        ctx.save()
        ctx.translate(0,self.menubar.height)
        text = self.buf.get_cairo_surface()
        ctx.set_source_surface(text,0,0)
        ctx.paint()
        ctx.restore()

        self.menubar.draw(ctx,0,0,WIDTH)

        self.decorator.render(self)
        self.flip()

    def finish_resize(self, msg):
        """Accept a resize."""
        if msg.width < 120 or msg.height < 120:
            self.resize_offer(max(msg.width,120),max(msg.height,120))
            return
        self.resize_accept(msg.width, msg.height)
        self.reinit()
        self.redraw_buf(True)
        self.draw()
        self.resize_done()
        self.flip()

    def scroll(self, amount):
        self.page += amount
        if self.page < 1:
            self.page = 1
        if self.page > self.page_count:
            self.page = self.page_count
        self.redraw_buf(False)


    def mouse_event(self, msg):
        if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE:
            window.close()
            sys.exit(0)
        x,y = msg.new_x - self.decorator.left_width(), msg.new_y - self.decorator.top_height()
        w,h = self.width - self.decorator.width(), self.height - self.decorator.height()

        if x >= 0 and x < w and y >= 0 and y < self.menubar.height:
            self.menubar.mouse_event(msg, x, y)
            return

        if msg.buttons & yutani.MouseButton.SCROLL_UP:
            self.scroll(-1)
            self.draw()
            return
        elif msg.buttons & yutani.MouseButton.SCROLL_DOWN:
            self.scroll(1)
            self.draw()
            return

    def keyboard_event(self, msg):
        if msg.event.action != yutani.KeyAction.ACTION_DOWN:
            return # Ignore anything that isn't a key down.
        if msg.event.key == b"q":
            self.close()
            sys.exit(0)