コード例 #1
0
ファイル: interface.py プロジェクト: Sam-C-W/Mappapp2
class MainScreen(GridLayout):
    """
    This class describes the main GUI window
    """
    def __init__(self, **kwargs):
        super(MainScreen, self).__init__(**kwargs)
        self.backend = Backend()
        self.initialize_keyboard()
        """---------------------A: this section of code controls the appearance of the main window-------------------"""

        self.cols = 3
        self.rows = 2
        self.padding = 20
        self.spacing = 10
        Window.size = (980, 700)
        self.control_down = False

        def window_color_updater(instance, value):
            self.rect.pos = instance.pos
            self.rect.size = instance.size
            self.rect2.pos = 20, instance.size[1] - self.rect2.texture.size[
                1] - 10

        with self.canvas.before:
            Color(1, 1, 1, 0.2)
            self.rect = Rectangle(pos=self.pos, size=(200, 200))
            lable_texture = self.map_title()
            self.rect2 = Rectangle(size=lable_texture.size,
                                   texture=lable_texture)
            self.bind(size=window_color_updater, pos=window_color_updater)
        """------------B: This section of code creates the tileset control menue and active tile display------------"""

        def handle_tileset_select(instance):
            target_tileset = self.backend.working_dir + "img_store/tilesets/" + instance.text + ".png"
            if target_tileset != self.backend.active_tile_set_file:
                self.backend.active_tile_set_file = target_tileset
                self.backend.update_active_tileset()
                self.redraw_tileset()
                if self.backend.check_active_layer_type():
                    self.backend.layerlist.new_layer(
                        self.backend.active_tile_set_file,
                        position=self.backend.active_layer + 1)
                    self.backend.active_layer += 1
                    self.populate_layer_list(layer_list)
                    self.backend.draw_map()
                    self.redraw_active_tile()
                current_set.text = self.backend.active_tile_set_file.split(
                    '/')[-1]
                tileset_dropdown.dismiss()
            else:
                tileset_dropdown.dismiss()

        def open_tile_set_dropdown(instance):
            tileset_dropdown.open(instance)

        tileset_menue = GridLayout(size_hint=(None, None),
                                   size=(300, 100),
                                   spacing=20,
                                   pos_hint=(None, 1),
                                   cols=3)
        tileset_select_shell = FloatLayout(size=(175, 100),
                                           size_hint=(None, None))
        tileset_dropdown = DropDown()
        available_sets = os.listdir(self.backend.working_dir +
                                    'img_store/tilesets')

        for i in available_sets:
            set_name = i[:len(i) - 4]
            set_btn = Button(text=set_name, size_hint_y=None, height=44)
            set_btn.bind(on_press=handle_tileset_select)
            tileset_dropdown.add_widget(set_btn)
        tileset_dropdown_btn = Button(text="Select",
                                      size=(175, 27),
                                      size_hint=(None, None),
                                      pos_hint={
                                          'x': 0,
                                          'y': .2
                                      })
        tileset_dropdown_btn.bind(on_release=open_tile_set_dropdown)
        current_set = Label(
            text=self.backend.active_tile_set_file.split('/')[-1],
            size=(175, 25),
            size_hint=(None, None),
            pos_hint={
                'x': 0,
                'y': .5
            })
        tileset_select_shell.add_widget(current_set)
        tileset_select_shell.add_widget(tileset_dropdown_btn)
        tileset_menue.add_widget(tileset_select_shell)

        tileset_menue.add_widget(Label(text="Active\nTile:", font_size='18sp'))
        active_tile_display = KvyImage()
        active_tile_display.texture = self.get_texture(
            self.backend.get_active_tile_image())
        tileset_menue.add_widget(active_tile_display)
        self.add_widget(tileset_menue)
        """-------------C: This section of code creates the map space control buttons and drawing tools--------------"""
        map_control = GridLayout(cols=5,
                                 size=(100, 100),
                                 size_hint=(1, None),
                                 padding=20,
                                 spacing=5,
                                 rows=2)

        def alter_map(instance):
            if instance.text == "Resize":
                self.popup_constructor_size("Resize Map").open()
            else:
                self.popup_constructor_size(
                    "New Map: !!UNSAVED PROGRESS WILL BE LOST!!").open()

        def save_load_export(instance):
            if instance.text == "Save":
                self.popup_constructor_file("Save Map")
            if instance.text == "Load":
                self.popup_constructor_file("Load Map")
            if instance.text == "Export":
                self.popup_constructor_file("Export Map")

        resize_map_btn = Button(text="Resize")
        new_map_btn = Button(text="New")
        save_btn = Button(text="Save")
        load_btn = Button(text="Load")
        export_btn = Button(text="Export")
        resize_map_btn.bind(on_press=alter_map)
        new_map_btn.bind(on_press=alter_map)
        for button in [save_btn, load_btn, export_btn]:
            button.bind(on_press=save_load_export)
        for button in [
                new_map_btn, resize_map_btn, save_btn, load_btn, export_btn
        ]:
            map_control.add_widget(button)

        def tool_logic(instance):
            if instance.text == "Grid":
                self.backend.grid_toggle = 1 if self.backend.grid_toggle == 0 else 0
                self.backend.undo_tracker += -1
                self.backend.undo_array = self.backend.undo_array[0:-1]
                self.redraw_map()
                if self.backend.grid_toggle:
                    tool_buttons[0].background_color = (0.5, 0.5, 0.5, 1.0)
                else:
                    tool_buttons[0].background_color = (1.0, 1.0, 1.0, 1.0)
            if instance.text == "Fill":
                self.backend.fill_toggle = 1 if self.backend.fill_toggle == 0 else 0
                self.backend.square_toggle = 0
                self.redraw_map()
                if self.backend.fill_toggle:
                    tool_buttons[1].background_color = (0.5, 0.5, 0.5, 1.0)
                    tool_buttons[2].background_color = (1.0, 1.0, 1.0, 1.0)
                else:
                    tool_buttons[1].background_color = (1.0, 1.0, 1.0, 1.0)
            if instance.text == "Sqr":
                self.backend.square_toggle = 1 if self.backend.square_toggle == 0 else 0
                self.backend.fill_toggle = 0
                if self.backend.square_toggle:
                    tool_buttons[2].background_color = (0.5, 0.5, 0.5, 1.0)
                    tool_buttons[1].background_color = (1.0, 1.0, 1.0, 1.0)
                else:
                    tool_buttons[2].background_color = (1.0, 1.0, 1.0, 1.0)
            if instance.text == "2x":
                self.backend.zoom_mod = 2 if self.backend.zoom_mod != 2 else 1
                self.backend.undo_tracker += -1
                self.backend.undo_array = self.backend.undo_array[0:-1]
                self.redraw_map()
                if self.backend.zoom_mod == 2:
                    tool_buttons[3].background_color = (0.5, 0.5, 0.5, 1.0)
                    tool_buttons[4].background_color = (1.0, 1.0, 1.0, 1.0)
                else:
                    tool_buttons[3].background_color = (1.0, 1.0, 1.0, 1.0)
            if instance.text == ".5x":
                self.backend.zoom_mod = 0.5 if self.backend.zoom_mod != 0.5 else 1
                self.backend.undo_tracker += -1
                self.backend.undo_array = self.backend.undo_array[0:-1]
                self.redraw_map()
                if self.backend.zoom_mod == 0.5:
                    tool_buttons[4].background_color = (0.5, 0.5, 0.5, 1.0)
                    tool_buttons[3].background_color = (1.0, 1.0, 1.0, 1.0)
                else:
                    tool_buttons[4].background_color = (1.0, 1.0, 1.0, 1.0)
            elif instance.text == "Img":
                self.popup_constructor_file('Import Image')

        tool_buttons = []
        for tool in [
                'Grid',
                'Fill',
                'Sqr',
                '2x',
                '.5x',
                'Img',
        ]:
            tool_btn = Button(text=tool)
            tool_btn.bind(on_press=tool_logic)
            tool_buttons.append(tool_btn)

        for i in range(0, 6, 2):
            button_shell = GridLayout(cols=2, spacing=5)
            button_shell.add_widget(tool_buttons[i])
            button_shell.add_widget(tool_buttons[i + 1])
            map_control.add_widget(button_shell)
        self.add_widget(map_control)
        """-------------------------D: This section of code creates an important spacer label------------------------"""
        self.add_widget(
            Label(text="\n\n\n\n[b]Layer Menue[/b]",
                  markup=True,
                  text_size=(200, 100),
                  font_size='20dp',
                  size_hint=(None, None),
                  size=(200, 100),
                  pos_hint=(1, 0)))
        """---------------E: This section of code creates the scrollable viewers for the map and tileset-------------"""
        map_scroller = self.get_map_scroller()
        tile_scroller = self.get_tileset_scroller()
        tileset = TilesetView(backend=self.backend, size_hint=(None, None))
        map_image = MapView(backend=self.backend, size_hint=(None, None))
        map_scroller.add_widget(map_image)
        tile_scroller.add_widget(tileset)
        self.add_widget(tile_scroller)
        self.add_widget(map_scroller)
        tileset.texture = self.get_texture(self.backend.active_tile_set)
        map_image.texture = self.get_texture(self.backend.draw_map())
        tileset.size = tileset.texture.size
        map_image.size = map_image.texture.size

        cursor_size = (self.backend.layerlist.get_grid_res(),
                       self.backend.layerlist.get_grid_res())
        cursor_size = tuple(val * self.backend.zoom_mod for val in cursor_size)
        map_cursor = Button(text="", size=cursor_size)
        tile_cursor = Button(text="", size=cursor_size)

        map_image.add_widget(map_cursor)

        tileset.add_widget(tile_cursor)

        def cursor_polling(instance, pos):

            res = self.backend.layerlist.get_grid_res()
            if map_scroller.collide_point(*pos):
                res = self.backend.layerlist.get_grid_res(
                ) * self.backend.zoom_mod
                pos = map_scroller.to_local(*pos)
                if map_image.collide_point(*pos):
                    tile_cursor.background_color = [0, 0, 0, 0]
                    map_cursor.background_color = [0, 0, 1, 0.3]
                    map_cursor.size = tuple(
                        val * self.backend.zoom_mod
                        for val in self.backend.active_tile_image.size)
                    if self.backend.check_active_layer_type():

                        pos = (pos[0] // res) * res, (pos[1] // res) * res - (
                            map_cursor.size[1] - res)
                    else:
                        pos = pos[0] - map_cursor.size[1] / 2, pos[1] - \
                              map_cursor.size[1] / 2
                    map_cursor.pos = pos
                else:
                    map_cursor.background_color = [0, 0, 0, 0]

            elif tile_scroller.collide_point(*pos):
                pos = tile_scroller.to_local(*pos)
                if tileset.collide_point(*pos):
                    tile_cursor.background_color = [0, 1, 0, 0.5]
                    map_cursor.background_color = [0, 0, 0, 0]
                    tile_cursor.size = cursor_size
                    pos = (pos[0] // res) * res, (pos[1] // res) * res
                    tile_cursor.pos = pos
                else:
                    tile_cursor.background_color = [0, 0, 0, 0]

        Window.bind(mouse_pos=cursor_polling)
        """-------------------------F: This section of code creates the layer control panel--------------------------"""
        layer_control_shell = GridLayout(rows=2,
                                         size_hint=(None, 1),
                                         size=(200, 200))
        layer_control_panel = GridLayout(rows=3,
                                         cols=3,
                                         size_hint=(None, None),
                                         size=(200, 40),
                                         pos=self.size,
                                         pos_hint=(None, None))
        layer_list = GridLayout(rows=1,
                                cols=1,
                                size=(200, 1),
                                size_hint=(None, 1))

        def modify_layer_list(instance):
            """
            This method handles button presses on the layer control buttons.
            :param instance:
            :return:
            """
            if instance.text == "New":
                self.backend.layerlist.new_layer(
                    self.backend.active_tile_set_file,
                    position=self.backend.active_layer + 1)
                self.backend.active_layer += 1
            elif instance.text == "Delete":
                self.backend.layerlist.del_layer(self.backend.active_layer)
                self.backend.active_layer += -1
                if self.backend.active_layer < 1:
                    if len(self.backend.layerlist.stack) <= 1:
                        self.backend.layerlist.new_layer(
                            self.backend.active_tile_set_file)
                        self.backend.active_layer += 1
                    else:
                        self.backend.active_layer = 1
                self.backend.active_tile_set_file = self.backend.layerlist.get_layer(
                    self.backend.active_layer).tileset
                self.backend.update_active_tileset()
                self.redraw_active_tile()
                self.redraw_tileset()
            elif instance.text == "Up":
                if self.backend.active_layer > 1:
                    self.backend.layerlist.move_up(self.backend.active_layer)
                    self.backend.active_layer += -1
            elif instance.text == "Down":
                if self.backend.active_layer < len(
                        self.backend.layerlist.stack) - 1:
                    self.backend.layerlist.move_down(self.backend.active_layer)
                    self.backend.active_layer += 1
            elif instance.text == "Rename":
                self.popup_constructor_rename().open()
            else:
                self.backend.layerlist.add_image_layer(
                    self.backend.active_tile_set_file,
                    position=self.backend.active_layer + 1)
                self.backend.active_layer += 1
            self.populate_layer_list(layer_list)
            self.redraw_map()

        new_layer_btn = Button(text="New")
        del_layer_btn = Button(text="Delete")
        new_layer_btn.bind(on_press=modify_layer_list)
        del_layer_btn.bind(on_press=modify_layer_list)
        up_btn = Button(text="Up")
        down_btn = Button(text="Down")
        rename_btn = Button(text="Rename")
        freedraw_btn = Button(text="Unsnap")
        for button in [
                rename_btn, new_layer_btn, up_btn, freedraw_btn, del_layer_btn,
                down_btn
        ]:
            button.bind(on_press=modify_layer_list)
            layer_control_panel.add_widget(button)
        layer_control_shell.add_widget(layer_control_panel)
        self.populate_layer_list(layer_list)
        layer_control_shell.add_widget(layer_list)
        self.add_widget(layer_control_shell)

    def populate_layer_list(self, widget):
        """
        This layer populates the list of displayed layers, highlighting the active layer in white
        :param widget:
        :return:
        """
        def set_active_layer(instance):
            words = instance.text.split(" ")
            self.backend.active_layer = int(words[-1])
            self.populate_layer_list(widget)
            self.backend.active_tile_set_file = self.backend.layerlist.get_layer(
                self.backend.active_layer).tileset
            self.backend.update_active_tileset()
            self.redraw_active_tile()
            self.redraw_tileset()
            self.children[len(self.children) - 1].children[2].children[1]. \
                text = text = self.backend.active_tile_set_file.split('/')[-1]

        widget.clear_widgets()
        for i, layer in enumerate(self.backend.layerlist.stack[1:]):
            widget.rows += 1
            layer_button = Button(text=f"{layer.name} at position {i + 1}")
            layer_button.bind(on_press=set_active_layer)
            if not isinstance(layer, Layer):
                layer_button.color = [0.5, 0.5, 1, 1]
            if i + 1 == self.backend.active_layer:
                layer_button.background_normal = ""
                if not isinstance(layer, Layer):
                    layer_button.color = [0, 0.4, 0.9, 1]
                else:
                    layer_button.color = (0, 0, 0, 1)
            widget.add_widget(layer_button)

    def redraw_active_tile(self):
        self.children[5].children[0].texture = self.get_texture(
            self.backend.get_active_tile_image())
        self.children[5].children[0].size = self.children[5].children[
            0].texture.size

    def popup_constructor_rename(self):
        """
        this method creates a pop-up dialogue to rename a layer
        :return:
        """
        def resolve(instance):
            """
            This method resolves the rename popup
            :param instance:
            :return:
            """
            new_name = instance.text
            if new_name.isalnum():
                self.backend.layerlist.set_name(new_name,
                                                self.backend.active_layer)
                self.populate_layer_list(self.children[0].children[0])
            pop.dismiss()
            self.initialize_keyboard()

        name_input = TextInput(text="Layername")
        name_input.multiline = False
        name_input.bind(on_text_validate=resolve)
        pop = Popup(title="Rename Layer:",
                    content=name_input,
                    size_hint=(None, None),
                    size=(500, 100),
                    auto_dismiss=True)
        return pop

    def popup_constructor_file(self, title):
        """
        generalized popup constructor for save/load/export and import image commands
        :param title:
        :return:
        """
        changed_dir = True  #set this to false when testing in IDE
        file_browser = FileChooserIconView()
        file_browser_text = TextInput(text="File.txt",
                                      multiline=False,
                                      size=(345, 30),
                                      size_hint=(None, None))
        file_browser.add_widget(file_browser_text)
        file_browser.path = self.backend.working_dir + 'Saved maps'
        file_browser_cancel_btn = Button(text="Cancel",
                                         size=(60, 30),
                                         size_hint=(None, None),
                                         pos=(415, 0))
        file_browser_confirm_btn = Button(text="blank",
                                          size=(60, 30),
                                          size_hint=(None, None),
                                          pos=(350, 0))
        if title == "Save Map":
            file_browser_text.text = file_browser.path + "/Untitled.text"
            file_browser_confirm_btn.text = "Save"
        elif title == "Load Map":
            file_browser_text.text = file_browser.path + "/Untitled.text"
            file_browser_confirm_btn.text = "Load"
        elif title == "Import Image":
            file_browser_text.text = file_browser.path + "/Untitled.png"
            file_browser_confirm_btn.text = "Import"
        else:
            file_browser_text.text = file_browser.path + "/Untitled.png"
            file_browser_confirm_btn.text = "Export"
        file_browser.add_widget(file_browser_cancel_btn)
        file_browser.add_widget(file_browser_confirm_btn)

        def resolve_dialogue(instance):
            try:
                if title == "Save Map":
                    if file_browser_text.text.split("\\")[-1] in [i.split("\\")[-1] for i in file_browser.files] or \
                            file_browser_text.text.split("/")[-1] in [i.split("\\")[-1] for i in file_browser.files]:

                        def overwrite_resolve(instance):
                            self.backend.save_to_file(file_browser_text.text)
                            self.backend.working_file = file_browser_text.text
                            self.backend.last_save = datetime.now()
                            self.update_map_title()
                            pop2.dismiss()
                            pop.dismiss()
                            self.initialize_keyboard()

                        overwrite_okay.bind(on_press=overwrite_resolve)
                        nonlocal overwrite_msg
                        overwrite_msg = Label(
                            text=
                            f"The file: \n{file_browser_text.text}\nwill be Overwritten"
                        )
                        pop2.open()
                    else:
                        self.backend.save_to_file(file_browser_text.text)
                        self.backend.working_file = file_browser_text.text
                        self.backend.last_save = datetime.now()
                        self.update_map_title()
                        pop.dismiss()
                        self.initialize_keyboard()
                elif title == "Load Map":
                    self.backend.load_from_file(file_browser_text.text)
                    self.redraw_map()
                    self.redraw_tileset()
                    self.redraw_active_tile()
                    self.populate_layer_list(self.children[0].children[0])
                    self.backend.working_file = file_browser_text.text
                    self.backend.last_save = datetime.now()
                    self.update_map_title()
                    pop.dismiss()
                    self.initialize_keyboard()
                elif title == "Export Map":
                    if file_browser_text.text.split("\\")[-1] in [i.split("\\")[-1] for i in file_browser.files] or \
                            file_browser_text.text.split("/")[-1] in [i.split("\\")[-1] for i in file_browser.files]:

                        def overwrite_resolve(instance):
                            self.backend.export(file_browser_text.text)
                            pop2.dismiss()
                            pop.dismiss()
                            self.initialize_keyboard()

                        overwrite_okay.bind(on_press=overwrite_resolve)
                        overwrite_msg.text = f"!WARNING!: The file: \n{file_browser_text.text}\nwill be Overwritten"
                        pop2.open()
                    else:
                        self.backend.export(file_browser_text.text)
                        pop.dismiss()
                        self.initialize_keyboard()
                elif title == "Import Image":
                    self.backend.import_img(file_browser_text.text)
                    self.populate_layer_list(self.children[0].children[0])
                    self.redraw_map()
                    pop.dismiss()
                    self.initialize_keyboard()

            except Exception as e:
                error = Popup(title=" An error Occured:",
                              content=(Label(text=e.__str__())),
                              size_hint=(None, None),
                              size=(400, 200),
                              auto_dismiss=True)
                error.open()

        def filecancel(instance):
            pop.dismiss()
            self.initialize_keyboard()

        def update_file_text(instance, path):
            nonlocal changed_dir
            changed_dir = True
            if title == "Save Map" or title == "Load Map":
                file_browser_text.text = file_browser.path + "/Untitled.text"
            else:
                file_browser_text.text = file_browser.path + "/Untitled.png"

        def update_file_text_select(instance, selection, touch):
            path = ""
            for i in selection:
                path = i
                file_browser_text.text = path
            if changed_dir == False:
                segments = path.split("\\")
                del segments[5]
                clean_path = "\\".join(segments)
                file_browser_text.text = clean_path

        file_browser.bind(on_submit=update_file_text_select,
                          path=update_file_text)
        file_browser_cancel_btn.bind(on_press=filecancel)
        file_browser_confirm_btn.bind(on_press=resolve_dialogue)

        pop = Popup(title=title,
                    content=file_browser,
                    size_hint=(None, None),
                    size=(500, 500),
                    auto_dismiss=True)
        pop.open()

        overwrite = GridLayout(rows=2)
        overwrite_msg = Label(
            text=
            f"!WARNING!: The file: \n{file_browser_text.text}\nwill be Overwritten"
        )
        overwrite_buttons = GridLayout(cols=2,
                                       size_hint=(1, None),
                                       size=(100, 40))
        overwrite_okay = Button(text="Overwrite",
                                size_hint=(1, None),
                                size=(100, 40))
        overwrite_cancel = Button(text="Cancel",
                                  size_hint=(1, None),
                                  size=(100, 40))
        overwrite_buttons.add_widget(overwrite_okay)
        overwrite_buttons.add_widget(overwrite_cancel)
        overwrite.add_widget(overwrite_msg)
        overwrite.add_widget(overwrite_buttons)
        overwrite_cancel.bind(on_press=lambda instance: pop2.dismiss())

        pop2 = Popup(title="Confirm File Overwrite",
                     content=overwrite,
                     size_hint=(None, None),
                     size=(500, 300),
                     auto_dismiss=True)

    def popup_constructor_size(self, title):
        """
        This popup creates a generic pop up with two text field inputs, used for height and width to update the size of
        the map, or create a new map of the specified size.
        :param title:
        :return:
        """
        grid_res = self.backend.layerlist.get_grid_res()
        self.map_width = self.backend.layerlist.get_size()[0]
        self.map_height = self.backend.layerlist.get_size()[1]

        def cancel_dialogue(instance):
            pop.dismiss()
            self.initialize_keyboard()

        def resolve_dialogue(instance):
            self.map_height = 1 if self.map_height < 2 else self.map_height
            self.map_width = 1 if self.map_height < 2 else self.map_width
            if title == "New Map: !!UNSAVED PROGRESS WILL BE LOST!!":
                self.backend.create_drawspace(1, 1)
                self.backend.layerlist.resize(
                    (self.map_width, self.map_height))
                self.redraw_map()
                self.populate_layer_list(self.children[0].children[0])
                self.backend.working_file = ""
                self.backend.last_save = ""
                self.update_map_title()
                pop.dismiss()
                self.initialize_keyboard()
            else:
                self.backend.layerlist.resize(
                    (self.map_width, self.map_height))
                self.redraw_map()
                pop.dismiss()
                Window.size = Window.size[0] - 1, Window.size[1] - 1
                self.initialize_keyboard()

        def store_width(instance, value):
            if instance.text != "":
                self.map_width = int(instance.text)

        def store_height(instance, value):
            if instance.text != "":
                self.map_height = int(instance.text)

        input_shell = GridLayout(rows=3, cols=2)
        input1 = TextInput(multiline=False, text=str(int(self.map_width)))
        input1.bind(text=store_width)
        input2 = TextInput(multiline=False, text=str(int(self.map_height)))
        input2.bind(text=store_height)
        input_shell.add_widget(Label(text="Grid Width"))
        input_shell.add_widget(Label(text="Grid Height"))
        input_shell.add_widget(input1)
        input_shell.add_widget(input2)
        okay_btn = Button(text="Okay")
        cancel_btn = Button(text="Cancel")
        okay_btn.bind(on_press=resolve_dialogue)
        cancel_btn.bind(on_press=cancel_dialogue)
        input_shell.add_widget(cancel_btn)
        input_shell.add_widget(okay_btn)
        pop = Popup(title=title,
                    content=input_shell,
                    size_hint=(None, None),
                    size=(400, 200),
                    auto_dismiss=True)
        return pop

    def redraw_map(self, target=None):
        """
        Simple method that redraws the full map and displays it on the gui.
        :return:
        """
        self.children[1].children[0].texture = self.get_texture(
            self.backend.draw_map(target))
        self.children[1].children[0].size = self.children[1].children[
            0].texture.size

    def redraw_tileset(self):
        """
        Simple method that redraws the tileset and displays it on the gui
        :return:
        """
        self.children[len(self.children) - 1].children[2].children[1].text = \
            self.backend.active_tile_set_file.split('/')[-1]
        self.children[2].children[0].texture = self.get_texture(
            self.backend.active_tile_set)
        self.children[2].children[0].size = self.children[2].children[
            0].texture.size

    def get_map_scroller(self):
        """
        returns a scroll view object for viewing the map or drawspace
        :return:
        """
        returnable = ScrollView(size_hint=(1, 1),
                                scroll_type=['bars'],
                                bar_width=16)
        return returnable

    def get_tileset_scroller(self):
        """
        returns a scroll view object for viewing the tilset
        :return:
        """
        returnable = ScrollView(size_hint=(None, 1),
                                size=(300, 300),
                                scroll_type=['bars'],
                                bar_width=16)
        return returnable

    def get_texture(self, image):
        """
        This method accepts a PIL image object and returns a kivy texture created from the image
        :param image:
        :return:
        """
        num_array = numpy.array(image)
        texture = Texture.create(size=image.size)
        texture.blit_buffer(num_array.tobytes(),
                            bufferfmt="ubyte",
                            colorfmt="rgba")
        texture.flip_vertical()
        return texture

    def run_undo(self):
        if self.backend.undo():
            self.children[1].children[0].texture = self.get_texture(
                self.backend.draw_map(not_undoing=False))
            self.children[1].children[0].size = self.children[1].children[
                0].texture.size
            self.backend.active_tile_set_file = self.backend.layerlist.get_layer(
                self.backend.active_layer).tileset
            self.backend.update_active_tileset()
            self.redraw_active_tile()
            self.redraw_tileset()
            self.populate_layer_list(self.children[0].children[0])

    def run_redo(self):
        if self.backend.redo():
            self.children[1].children[0].texture = self.get_texture(
                self.backend.draw_map(not_undoing=False))
            self.children[1].children[0].size = self.children[1].children[
                0].texture.size
            self.backend.active_tile_set_file = self.backend.layerlist.get_layer(
                self.backend.active_layer).tileset
            self.backend.update_active_tileset()
            self.redraw_active_tile()
            self.redraw_tileset()
            self.populate_layer_list(self.children[0].children[0])

    def map_title(self):
        if self.backend.working_file:
            # map_name = self.backend.working_file[0:-5]
            # map_name_clean = ""
            # for i in range(len(map_name) - 1, 0, -1):
            #     char = map_name[i]
            #     if char.isalnum():
            #         map_name_clean += char
            #     else:
            #         break
            # map_name_clean = map_name_clean[::-1]
            map_name = self.backend.working_file.replace("\\", "/")
            map_name_clean = map_name.split("/")[-1][0:-5]
            title_string = map_name_clean + ", Last Saved: " + self.backend.last_save.strftime(
                "%Y-%m-%d %H:%M:%S")
        else:
            title_string = "Unsaved"
        title_label = CoreLabel(text=title_string, font_size=20)
        title_label.refresh()
        return title_label.texture

    def update_map_title(self):
        text = self.map_title()
        self.rect2.texture = text
        self.rect2.size = text.size

    def no_dialogue_save(self):
        try:
            self.backend.save_to_file(self.backend.working_file)
            self.backend.last_save = datetime.now()
            self.update_map_title()
            print('Saved')
        except:
            print('failed to save')

    def initialize_keyboard(self):
        def _keyboard_closed():
            self._keyboard.unbind(on_key_down=on_keyboard_down)
            self._keyboard.unbind(on_key_up=on_keyboard_up)
            self._keyboard = None

        def on_keyboard_down(self, key, scancode, codepoint):
            if key == (305, 'lctrl'):
                self.control_down = True
            return True

        def on_keyboard_up(self, key):
            if key == (305, 'lctrl'):
                self.control_down = False
            if key == (122, 'z') and self.control_down:
                self.target.run_undo()
            elif key == (121, 'y') and self.control_down:
                self.target.run_redo()
            elif key == (115, 's') and self.control_down:
                self.target.no_dialogue_save()
            return True

        self._keyboard = Window.request_keyboard(_keyboard_closed, self)
        self._keyboard.bind(on_key_up=on_keyboard_up)
        self._keyboard.bind(on_key_down=on_keyboard_down)