1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! ECS rendering bundle

use crate::{
    BlinkSystem, CacheSelectionOrderSystem, FontAsset, NoCustomUi, ResizeSystemDesc,
    SelectionKeyboardSystemDesc, SelectionMouseSystemDesc, TextEditingInputSystemDesc,
    TextEditingMouseSystemDesc, ToNativeWidget, UiButtonActionRetriggerSystemDesc,
    UiButtonSystemDesc, UiLoaderSystemDesc, UiMouseSystem, UiSoundRetriggerSystemDesc,
    UiSoundSystemDesc, UiTransformSystemDesc, WidgetId,
};
use amethyst_assets::Processor;
use amethyst_core::{
    bundle::SystemBundle,
    ecs::prelude::{DispatcherBuilder, World},
    SystemDesc,
};
use amethyst_error::Error;
use amethyst_input::BindingTypes;
use derive_new::new;
use std::marker::PhantomData;

/// UI bundle
///
/// Will register all necessary components and systems needed for UI, along with any resources.
/// The generic type T represent the T generic parameter of the InputHandler<T>.
///
/// Will fail with error 'No resource with the given id' if the InputBundle is not added.
#[derive(new, Debug)]
pub struct UiBundle<T: BindingTypes, C = NoCustomUi, W = u32, G = ()> {
    #[new(default)]
    _marker: PhantomData<(T, C, W, G)>,
}

impl<'a, 'b, T, C, W, G> SystemBundle<'a, 'b> for UiBundle<T, C, W, G>
where
    T: BindingTypes,
    C: ToNativeWidget,
    W: WidgetId,
    G: Send + Sync + PartialEq + 'static,
{
    fn build(
        self,
        world: &mut World,
        builder: &mut DispatcherBuilder<'a, 'b>,
    ) -> Result<(), Error> {
        builder.add(
            UiLoaderSystemDesc::<<C as ToNativeWidget>::PrefabData, W>::default().build(world),
            "ui_loader",
            &[],
        );
        builder.add(
            UiTransformSystemDesc::default().build(world),
            "ui_transform",
            &["transform_system"],
        );
        builder.add(
            Processor::<FontAsset>::new(),
            "font_processor",
            &["ui_loader"],
        );
        builder.add(
            CacheSelectionOrderSystem::<G>::new(),
            "selection_order_cache",
            &[],
        );
        builder.add(
            SelectionMouseSystemDesc::<G, T>::default().build(world),
            "ui_mouse_selection",
            &[],
        );
        builder.add(
            SelectionKeyboardSystemDesc::<G>::default().build(world),
            "ui_keyboard_selection",
            // Because when you press tab, you want to override the previously selected elements.
            &["ui_mouse_selection"],
        );
        builder.add(
            TextEditingMouseSystemDesc::default().build(world),
            "ui_text_editing_mouse_system",
            &["ui_mouse_selection", "ui_keyboard_selection"],
        );
        builder.add(
            TextEditingInputSystemDesc::default().build(world),
            "ui_text_editing_input_system",
            // Hard requirement. The system assumes the text to edit is selected.
            &["ui_mouse_selection", "ui_keyboard_selection"],
        );
        builder.add(
            ResizeSystemDesc::default().build(world),
            "ui_resize_system",
            &[],
        );
        builder.add(
            UiMouseSystem::<T>::new(),
            "ui_mouse_system",
            &["ui_transform"],
        );
        builder.add(
            UiButtonSystemDesc::default().build(world),
            "ui_button_system",
            &["ui_mouse_system"],
        );

        builder.add(
            UiButtonActionRetriggerSystemDesc::default().build(world),
            "ui_button_action_retrigger_system",
            &["ui_button_system"],
        );
        builder.add(
            UiSoundSystemDesc::default().build(world),
            "ui_sound_system",
            &[],
        );
        builder.add(
            UiSoundRetriggerSystemDesc::default().build(world),
            "ui_sound_retrigger_system",
            &["ui_sound_system"],
        );

        // Required for text editing. You want the cursor image to blink.
        builder.add(BlinkSystem, "blink_system", &[]);

        Ok(())
    }
}