Перетаскивание (Drag and Drop)

Ren'Py включает в себя displayable-объекты для перетаскивания, которые позволяют перемещать элементы по экрану с помощью мыши. Некоторые из применений перетаскивания:

  • Возможность пользователям изменять положение окон с сохранением их позиций.

  • Карточные игры, в которых требуется перетаскивать карты по экрану (например, пасьянс).

  • Системы инвентаря.

  • Системы изменения порядка перетаскиванием.

Displayable-объекты для перетаскивания позволяют реализовать эти и другие сценарии использования. Здесь задействованы два класса. Класс Drag представляет собой нечто, что можно перетаскивать по экрану, нечто, на что можно сбросить перетаскиваемый объект, или нечто, что может выполнять обе эти функции. Класс DragGroup представляет собой группу Drag-объектов. Чтобы перетаскивание произошло, оба Drag-объекта должны быть частью одной и той же drag-группы.

Систему перетаскивания можно использовать либо через язык экранов, либо непосредственно как displayable-объекты. Имеет смысл использовать язык экранов, когда вам не нужно обращаться к созданным Drag-объектам после их создания. Это может быть случай, когда перетаскиваемый объект представляет собой окно, которое пользователь размещает на экране. Если вам нужно обращаться к drag-объектам после их создания, то часто лучше создавать Drag-объекты напрямую и добавлять их в DragGroup.

Сбрасывание

Ren'Py может обрабатывать сбрасывание объекта двумя способами:

  • Если mouse_drop имеет значение true, перетаскиваемый объект будет сброшен на тот droppable-объект, который находится непосредственно под курсором мыши.

  • Если mouse_drop имеет значение false (по умолчанию), сбрасывание произойдет на тот droppable-объект, с которым у перетаскиваемого объекта наибольшее перекрытие.

В отличие от начала перетаскивания, где используется focus_mask, при сбрасывании учитываются полные прямоугольные области перетаскиваемого и принимающего объектов, включая любые прозрачные пиксели. Вам может потребоваться спроектировать ваши displayable-объекты для перетаскивания с учётом этого, делая их в основном прямоугольной формы.

Displayable-объекты

class Drag(d=None, drag_name=None, draggable=True, droppable=True, drag_raise=True, dragging=None, dragged=None, dropped=None, drag_handle=(0.0, 0.0, 1.0, 1.0), drag_joined=..., clicked=None, hovered=None, unhovered=None, mouse_drop=False, **properties)

Отображаемый объект, представляющий объект, который можно перетаскивать в пределах его охватывающей области. Drag также может представлять область, на которую можно перетащить другие объекты Drag.

Drag можно перемещать внутри его родителя. Как правило, его родителем должен быть либо Fixed(), либо DragGroup.

Drag имеет одного потомка. Состояние потомка отражает статус операции перетаскивания:

  • selected_hover - когда его перетаскивают.

  • selected_idle - когда на него можно что-то перетащить.

  • hover - когда перетаскиваемый объект будет перетаскиваться при щелчке мыши.

  • idle - в противном случае.

Область захвата – это прямоугольник внутри потомка. Курсор мыши должен находиться над пикселем внутри этой области для начала перетаскивания или щелчка. Если свойство focus_mask равно True, этот пиксель не должен быть прозрачным.

Новый созданный перетаскиваемый объект добавляется в DragGroup по умолчанию. Перетаскиваемый объект может находиться только в одной DragGroup – если его добавить во вторую группу, он удаляется из первой.

Когда Drag отрисовывается впервые, если его положение не может быть определено из DragGroup, в которой он находится, положение его верхнего левого угла вычисляется с использованием стандартного алгоритма компоновки. Как только это положение вычислено, свойства компоновки игнорируются в пользу положения, хранящегося внутри Drag.

Трансформы не следует применять к Drag напрямую. Вместо этого применяйте transform к потомку Drag.

d

Если указан, потомок этого Drag. Drag использует стиль потомка вместо этого параметра, если он не None.

drag_name

Если не None, имя этого перетаскиваемого объекта. Оно доступно как свойство drag_name перетаскиваемых объектов. Если Drag с таким же именем находится или находился в DragGroup, начальное положение этого Drag будет взято из того перетаскиваемого объекта.

draggable

Если true, Drag можно перетаскивать по экрану с помощью мыши.

droppable

Если true, другие объекты Drag можно перетаскивать на этот Drag.

drag_raise

Если true, этот Drag поднимается наверх, когда его перетаскивают. Если он соединён с другими объектами Drag, все соединённые объекты поднимаются.

activated

Обратный вызов (или список обратных вызовов), который вызывается, когда мышь нажимается на перетаскиваемом объекте. Вызывается с одним аргументом, списком объектов Drag, которые перетаскиваются. Возвращаемое значение этого обратного вызова игнорируется.

dragging

Обратный вызов (или список обратных вызовов), который вызывается, когда Drag перетаскивается. Вызывается с одним аргументом, списком объектов Drag, которые перетаскиваются. Если обратный вызов возвращает значение, отличное от None, это значение возвращается как результат взаимодействия.

dragged

Обратный вызов (или список обратных вызовов), который вызывается, когда Drag был перетащен. Вызывается с двумя аргументами. Первый – это список объектов Drag, которые перетаскиваются. Второй – это либо Drag, на который происходит перетаскивание, либо None, если перетаскивание не произошло. Если обратный вызов возвращает значение, отличное от None, это значение возвращается как результат взаимодействия.

dropped

Обратный вызов (или список обратных вызовов), который вызывается, когда на этот Drag что-то перетаскивают. Вызывается с двумя аргументами. Первый – это Drag, на который перетаскивают. Второй – это список объектов Drag, которые перетаскиваются. Если обратный вызов возвращает значение, отличное от None, это значение возвращается как результат взаимодействия.

Когда обратные вызовы dragged и dropped срабатывают для одного и того же события, обратный вызов dropped вызывается только если dragged возвращает None.

clicked

Обратный вызов, который вызывается, когда по Drag щёлкают без перемещения. Вызывается с одним аргументом, Drag, по которому щёлкнули. Droppable также может быть сфокусирован и щёлкнут. Если обратный вызов возвращает значение, отличное от None, это значение возвращается как результат взаимодействия.

alternate

Действие, которое выполняется, когда по Drag щёлкают правой кнопкой мыши (на десктопе) или долго нажимают без перемещения (на мобильных устройствах). Может потребоваться увеличить config.longpress_duration, если это срабатывает слишком рано на мобильных платформах.

hovered

Действие для выполнения, когда перетаскиваемый объект получает фокус.

unhovered

Действие для выполнения, когда перетаскиваемый объект теряет фокус.

tooltip

Подсказка, отображаемая при наведении на перетаскиваемый объект.

drag_handle

Кортеж (x, y, ширина, высота), задающий положение области захвата внутри потомка. Этот кортеж принимает позиции.

drag_joined

Вызывается с текущим Drag в качестве аргумента. Ожидается, что он вернёт список кортежей [ (drag, x, y) ], задающих перетаскиваемые объекты, которые нужно перетаскивать как единое целое. x и y – это смещения перетаскиваемых объектов относительно друг друга, они не относительны к углу этого Drag. drag – это либо объект Drag для соединения, либо drag_name такого Drag.

drag_offscreen

Определяет условия, при которых перетаскиваемый объект можно перетаскивать за пределы экрана. Разрешение перетаскивания за пределы экрана может быть опасно использовать с drag_joined или перетаскиваемыми объектами, которые могут изменять размер, так как они могут полностью покинуть экран без возможности вернуть их обратно.

Должен быть одним из:

False

Запретить перетаскивание за пределы экрана. (По умолчанию)

True

Разрешить перетаскивание за пределы экрана в любом направлении.

"horizontal"

Разрешить перетаскивание за пределы экрана только в горизонтальном направлении.

"vertical"

Разрешить перетаскивание за пределы экрана только в вертикальном направлении.

(width, height)

И width, и height должны быть целыми числами. Перетаскиваемый объект можно перетаскивать за пределы экрана, пока его часть размером (width, height) остаётся на экране. Таким образом, (100, 100) гарантирует, что область отображаемого объекта размером не менее 100x100 пикселей останется на экране, даже если остальная часть отображаемого объекта может быть перетащена за его пределы. Установка этого значения равным ширине и высоте перетаскиваемого отображаемого объекта эквивалентна полному запрету перетаскивания за пределы экрана.

(min_x, max_x, min_y, max_y)

Где каждый из min_x, max_x, min_y и max_y является целым числом. min_x – это количество пикселей от левой границы, а max_x – количество пикселей от правой границы. То же самое относится к min_y и max_y для верхней и нижней границ соответственно. Перетаскиваемый объект можно перемещать до тех пор, пока один из его краёв не достигнет указанной границы. (0, 0, 0, 0) эквивалентно полному запрету перетаскивания за пределы экрана.

Например, (-100, 200, 0, 0) позволит перетаскивать объект на 100 пикселей за левый край экрана и на 200 пикселей за правый край экрана, но не позволит перетаскивать его за верхний или нижний края.

Это также можно использовать для ограничения перетаскиваемого объекта в пределах экрана. (200, -200, 200, -200) позволит перемещать объект только в пределах 200 пикселей от краёв экрана.

Это можно представить как дополнительную "границу" вокруг перетаскиваемого объекта, которая может выходить за пределы экрана и ограничивает перемещение объекта.

callable

Для drag_offscreen можно предоставить вызываемый объект. Он должен принимать два аргумента: позицию x и y, которые представляют перетащенное положение верхнего левого угла перетаскиваемого объекта, и должен возвращать кортеж (x, y), который является новым положением (x, y), в котором должен находиться объект. Этот вызываемый объект вызывается часто, всякий раз, когда перетаскиваемый объект перемещается. Например, следующая функция привязывает объект к сетке каждые 300 пикселей:

def drag_snap(x, y):

    if y < 300:
        y = 0
    elif y < 600:
        y = 300
    else:
        y = 600

    return 200, y
mouse_drop

Если true, перетаскиваемый объект будет сброшен на первый droppable под курсором. Если false, (по умолчанию), объект будет сброшен на droppable с наибольшей степенью перекрытия.

drop_allowable

Обратный вызов, который вызывается, чтобы определить, позволит ли этот drop сбросить на него текущие перетаскиваемые объекты. Он вызывается с двумя аргументами. Первый – это Drag, который определяет его чувствительность. Второй – это список объектов Drag, которые перетаскиваются.

snapped

Обратный вызов (или список обратных вызовов), который вызывается, когда Drag завершает анимацию привязки. Он вызывается с четырьмя аргументами. Первый – это Drag, который проходил анимацию привязки. Второй и третий – это координаты x и y, к которым Drag должен был привязаться. Четвёртый – это логическое значение, которое равно True, если анимация привязки была успешно завершена, и False, если она была прервана (например, если его схватили в середине привязки). Например, следующая функция устанавливает start_x и start_y перетаскиваемого объекта в его предполагаемое конечное положение, если анимация привязки была прервана:

def snapped_callback(drag, x, y, completed):
    if not completed:
        drag.start_x = x
        drag.start_y = y

За исключением d, все параметры доступны как поля (с тем же именем) на объекте Drag. Кроме того, после отрисовки перетаскиваемого объекта становятся доступными следующие поля:

x, y

Положение Drag относительно его родителя, в пикселях.

start_x, start_y

Начальная позиция перетаскивания Drag относительно его родителя, в пикселях.

grab_x, grab_y

Позиции x и y, относительно его родителя, где перетаскиваемый объект был поднят, в пикселях.

last_drop

Последний Drag, на который текущий Drag может быть сброшен, если его отпустить, или None, если в данный момент нет подходящего Drag.

snapping

True, если этот Drag находится в процессе анимации привязки.

w, h

Ширина и высота потомка Drag, в пикселях.

bottom()

Опускает этот отображаемый объект в самый низ его drag_group.

set_child(d)

Изменяет потомка этого drag на d.

snap(x, y, delay=0, warper=None)

Изменяет положение перетаскиваемого объекта. Если он не отображается, изменение положения происходит мгновенно. В противном случае, изменение положения занимает delay секунд и использует опциональный warper. Если warper не указан, переход будет линейным.

top()

Поднимает этот отображаемый объект на самый верх его drag_group.

class DragGroup(*children, **properties)

Представляет группу объектов Drag. Drag ограничен границами своей DragGroup. Перетаскивание работает только между объектами Drag, находящимися в одной и той же DragGroup. Объекты Drag могут быть подняты наверх, только если они находятся внутри DragGroup.

DragGroup компонуется как Fixed().

Все позиционные параметры конструктора DragGroup должны быть объектами Drag, которые добавляются в DragGroup.

min_overlap

Целое число, означающее минимальное количество пикселей перекрытия, чтобы сброс был разрешён.

add(child)

Добавляет child, который должен быть Drag, в эту DragGroup. Этот потомок будет добавлен поверх всех других потомков этой DragGroup.

get_child_by_name(name)

Возвращает первого потомка этой DragGroup, у которого drag_name равно name.

remove(child)

Удаляет child из этой DragGroup.

Примеры

Пример экрана say, который позволяет пользователю выбирать расположение окна, перетаскивая его по экрану.:

screen say(who, what):

    drag:
        drag_name "say"
        yalign 1.0
        drag_handle (0, 0, 1.0, 30)

        xalign 0.5

        window id "window":
            # Ensure that the window is smaller than the screen.
            xmaximum 600

            has vbox

            if who:
                text who id "who"

            text what id "what"

Вот более сложный пример, который показывает, как перетаскивание может использоваться для влияния на игровой процесс. Он демонстрирует, как с помощью перетаскивания можно отправить персонажа в определённое место.:

init python:

    def detective_dragged(drags, drop):

        if not drop:
            return

        store.detective = drags[0].drag_name
        store.city = drop.drag_name

        return True

screen send_detective_screen:

    # A map as background.
    add "europe.jpg"

    # A drag group ensures that the detectives and the cities can be
    # dragged to each other.
    draggroup:

        # Our detectives.
        drag:
            drag_name "Ivy"
            droppable False
            dragged detective_dragged
            xpos 100 ypos 100

            add "ivy.png"
        drag:
            drag_name "Zack"
            droppable False
            dragged detective_dragged
            xpos 150 ypos 100

            add "zack.png"

        # The cities they can go to.
        drag:
            drag_name "London"
            draggable False
            xpos 450 ypos 140

            add "london.png"

        drag:
            drag_name "Paris"
            draggable False
            xpos 500 ypos 280

            add "paris.png"

label send_detective:
    "We need to investigate! Who should we send, and where should they go?"

    call screen send_detective_screen

    "Okay, we'll send [detective] to [city]."

Для правильной реализации более сложных систем требуются значительные навыки программирования.

Выражение as можно использовать для привязки drag-объекта к переменной, которую затем можно использовать для вызова методов этого объекта.

screen snap():

    drag:
        as carmen
        draggable True
        xpos 100 ypos 100
        frame:
            style "empty"
            background "carmen.png"
            xysize (100, 100)

            vbox:
                textbutton "London" action Function(carmen.snap, 450, 140, 1.0)
                textbutton "Paris" action Function(carmen.snap, 500, 280, 1.0)