~haowenl/vorg-windows

475675cbb96c5683641fb75cdf15071463c2d3b1 — Haowen Liu 11 months ago e0afd10
Extract video data editor
M vorg-windows/ImportEditPage.idl => vorg-windows/ImportEditPage.idl +0 -2
@@ 18,8 18,6 @@ namespace vorg_windows
        get;
    };

    void ClearState();

    event Windows.Foundation.TypedEventHandler<ImportEditPage, Object> EditFinished;
}
} // namespace vorg_windows

M vorg-windows/ImportEditPage.xaml => vorg-windows/ImportEditPage.xaml +5 -184
@@ 43,191 43,12 @@
                BorderThickness="0"
                CornerRadius="16"
                Source="{x:Bind Path=State.MediaSource, Mode=OneWay}" />
            <Grid
                Margin="0,16,0,0"
                ColumnSpacing="16"
                RowSpacing="8">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>

                <!--  Title  -->
                <TextBlock Grid.Row="0" Grid.Column="0">Title</TextBlock>
                <TextBox
                    x:Name="TitleInput"
                    Grid.Row="1"
                    Grid.Column="0" />

                <!--  Studio  -->
                <TextBlock Grid.Row="0" Grid.Column="1">Studio</TextBlock>
                <AutoSuggestBox
                    x:Name="StudioInput"
                    Grid.Row="1"
                    Grid.Column="1"
                    TextChanged="StudioInput_TextChanged" />
            </Grid>

            <Grid
                Margin="0,16,0,0"
                ColumnSpacing="16"
                RowSpacing="8">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>

                <!--  Actors  -->
                <TextBlock Grid.Row="0" Grid.Column="0">Actors</TextBlock>
                <Grid
                    Grid.Row="1"
                    Grid.Column="0"
                    ColumnSpacing="8">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <AutoSuggestBox
                        x:Name="ActorNameInput"
                        Grid.Column="0"
                        HorizontalAlignment="Stretch"
                        KeyUp="ActorNameInput_KeyUp"
                        TextChanged="ActorNameInput_TextChanged" />
                    <Button
                        x:Name="ActorsSubmitButton"
                        Grid.Column="1"
                        VerticalAlignment="Stretch"
                        Click="ActorsSubmitButton_Click"
                        Content="&#xE710;"
                        FontFamily="Segoe Fluent Icons"
                        IsTabStop="False" />
                </Grid>
                <ListView
                    Grid.Row="2"
                    Grid.Column="0"
                    ItemsSource="{x:Bind Actors}"
                    SelectionMode="Single">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate x:DataType="x:String">
                            <UserControl PointerEntered="UserControl_PointerEntered" PointerExited="UserControl_PointerExited">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>
                                    <TextBlock
                                        Grid.Column="0"
                                        VerticalAlignment="Center"
                                        Text="{x:Bind}" />
                                    <Button
                                        x:Name="ActorsDeleteButton"
                                        Grid.Column="1"
                                        Click="ActorsDeleteButton_Click"
                                        Content="&#xE711;"
                                        FontFamily="Segoe Fluent Icons"
                                        Visibility="Collapsed" />
                                    <VisualStateManager.VisualStateGroups>
                                        <VisualStateGroup x:Name="CommonStates">
                                            <VisualState x:Name="HoverButtonsHidden" />
                                            <VisualState x:Name="HoverButtonsShown">
                                                <VisualState.Setters>
                                                    <Setter Target="ActorsDeleteButton.Visibility" Value="Visible" />
                                                </VisualState.Setters>
                                            </VisualState>
                                        </VisualStateGroup>
                                    </VisualStateManager.VisualStateGroups>
                                </Grid>
                            </UserControl>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                    <ItemsControl.ItemContainerStyle>
                        <Style BasedOn="{StaticResource DefaultListViewItemStyle}" TargetType="ListViewItem">
                            <Setter Property="IsTabStop" Value="False" />
                        </Style>
                    </ItemsControl.ItemContainerStyle>
                </ListView>

                <!--  Tags  -->
                <TextBlock Grid.Row="0" Grid.Column="1">Tags</TextBlock>
                <Grid
                    Grid.Row="1"
                    Grid.Column="1"
                    ColumnSpacing="8">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <AutoSuggestBox
                        x:Name="TagNameInput"
                        Grid.Column="0"
                        HorizontalAlignment="Stretch"
                        KeyUp="TagNameInput_KeyUp"
                        TextChanged="TagNameInput_TextChanged" />
                    <Button
                        x:Name="TagsSubmitButton"
                        Grid.Column="1"
                        VerticalAlignment="Stretch"
                        Click="TagsSubmitButton_Click"
                        Content="&#xE710;"
                        FontFamily="Segoe Fluent Icons"
                        IsTabStop="False" />
                </Grid>
                <ListView
                    Grid.Row="2"
                    Grid.Column="1"
                    ItemsSource="{x:Bind Tags}"
                    SelectionMode="Single">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate x:DataType="x:String">
                            <UserControl PointerEntered="UserControl_PointerEntered" PointerExited="UserControl_PointerExited">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>
                                    <TextBlock
                                        Grid.Column="0"
                                        VerticalAlignment="Center"
                                        Text="{x:Bind}" />
                                    <Button
                                        x:Name="TagsDeleteButton"
                                        Grid.Column="1"
                                        Click="TagsDeleteButton_Click"
                                        Content="&#xE711;"
                                        FontFamily="Segoe Fluent Icons"
                                        Visibility="Collapsed" />
                                    <VisualStateManager.VisualStateGroups>
                                        <VisualStateGroup x:Name="CommonStates">
                                            <VisualState x:Name="HoverButtonsHidden" />
                                            <VisualState x:Name="HoverButtonsShown">
                                                <VisualState.Setters>
                                                    <Setter Target="TagsDeleteButton.Visibility" Value="Visible" />
                                                </VisualState.Setters>
                                            </VisualState>
                                        </VisualStateGroup>
                                    </VisualStateManager.VisualStateGroups>
                                </Grid>
                            </UserControl>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                    <ItemsControl.ItemContainerStyle>
                        <Style BasedOn="{StaticResource DefaultListViewItemStyle}" TargetType="ListViewItem">
                            <Setter Property="IsTabStop" Value="False" />
                        </Style>
                    </ItemsControl.ItemContainerStyle>
                </ListView>
            </Grid>

            <local:VideoDataEditor />
            <local:VideoDataEditor
                x:Name="Editor"
                ActorTextChanged="VideoDataEditor_ActorTextChanged"
                StudioTextChanged="VideoDataEditor_StudioTextChanged"
                TagTextChanged="VideoDataEditor_TagTextChanged" />

            <StackPanel
                Margin="0,16,0,0"

M vorg-windows/ImportEditPage.xaml.cpp => vorg-windows/ImportEditPage.xaml.cpp +9 -140
@@ 35,95 35,34 @@ vorg_windows::ImportEditPageState ImportEditPage::State() const
    return mState;
}

void ImportEditPage::UserControl_PointerEntered(const Windows::Foundation::IInspectable &sender,
                                                const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &)
{
    Microsoft::UI::Xaml::Controls::UserControl userControl = sender.as<Microsoft::UI::Xaml::Controls::UserControl>();
    VisualStateManager::GoToState(userControl, L"HoverButtonsShown", false);
}

void ImportEditPage::UserControl_PointerExited(const Windows::Foundation::IInspectable &sender,
                                               const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &)
{
    Microsoft::UI::Xaml::Controls::UserControl userControl = sender.as<Microsoft::UI::Xaml::Controls::UserControl>();
    VisualStateManager::GoToState(userControl, L"HoverButtonsHidden", false);
}

void ImportEditPage::ActorsDeleteButton_Click(const Windows::Foundation::IInspectable &sender,
                                              const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    Microsoft::UI::Xaml::Controls::Button button = sender.as<Microsoft::UI::Xaml::Controls::Button>();
    Windows::Foundation::IInspectable dataContext = button.DataContext();
    hstring itemName = dataContext.as<hstring>();
    DeleteFromVector(mActors, itemName);
}

void ImportEditPage::TagsDeleteButton_Click(const Windows::Foundation::IInspectable &sender,
                                            const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    Microsoft::UI::Xaml::Controls::Button button = sender.as<Microsoft::UI::Xaml::Controls::Button>();
    Windows::Foundation::IInspectable dataContext = button.DataContext();
    hstring itemName = dataContext.as<hstring>();
    DeleteFromVector(mTags, itemName);
}

void ImportEditPage::ActorsSubmitButton_Click(const Windows::Foundation::IInspectable &,
                                              const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    AddToActors(ActorNameInput().Text());
}

void ImportEditPage::TagsSubmitButton_Click(const Windows::Foundation::IInspectable &,
                                            const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    AddToTags(TagNameInput().Text());
}

void ImportEditPage::ActorNameInput_KeyUp(Windows::Foundation::IInspectable const &,
                                          Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const &e)
{
    if (e.Key() == Windows::System::VirtualKey::Enter)
    {
        AddToActors(ActorNameInput().Text());
    }
}

void ImportEditPage::TagNameInput_KeyUp(Windows::Foundation::IInspectable const &,
                                        Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const &e)
{
    if (e.Key() == Windows::System::VirtualKey::Enter)
    {
        AddToTags(TagNameInput().Text());
    }
}

void ImportEditPage::SubmitButton_Click(const Windows::Foundation::IInspectable &,
                                        const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    // Add currrent Actor and Tag
    AddToActors(ActorNameInput().Text());
    AddToTags(TagNameInput().Text());
    Editor().AddAllCurrent();

    // Import the file
    mApp->ImportNext(TitleInput().Text(), StudioInput().Text(), mActors, mTags);
    mApp->ImportNext(Editor().State().Title(), Editor().State().Studio(), Editor().State().Actors(),
                     Editor().State().Tags());
    Editor().ClearState();
    mEditFinishedEvent(*this, nullptr);
}

void ImportEditPage::StudioInput_TextChanged(
void ImportEditPage::VideoDataEditor_StudioTextChanged(
    const winrt::Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
    const winrt::Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &)
{
    sender.ItemsSource(mApp->CompleteStudios(sender.Text()));
}

void ImportEditPage::ActorNameInput_TextChanged(
void ImportEditPage::VideoDataEditor_ActorTextChanged(
    const winrt::Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
    const winrt::Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &)
{
    sender.ItemsSource(mApp->CompleteActors(sender.Text()));
}

void ImportEditPage::TagNameInput_TextChanged(
void ImportEditPage::VideoDataEditor_TagTextChanged(
    const winrt::Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
    const winrt::Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &)
{


@@ 134,6 73,7 @@ void ImportEditPage::BackButton_Click(const Windows::Foundation::IInspectable &,
                                      const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    mApp->CancelImport();
    Editor().ClearState();
    mEditFinishedEvent(*this, nullptr);
}



@@ 141,6 81,7 @@ void ImportEditPage::SkipButton_Click(const Windows::Foundation::IInspectable &,
                                      const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    mApp->Skip();
    Editor().ClearState();
    mEditFinishedEvent(*this, nullptr);
}



@@ 155,76 96,4 @@ void ImportEditPage::EditFinished(const event_token &token)
{
    mEditFinishedEvent.remove(token);
}

void ImportEditPage::ClearState()
{
    TitleInput().Text(L"");
    StudioInput().Text(L"");
    ActorNameInput().Text(L"");
    TagNameInput().Text(L"");
    mActors.Clear();
    mTags.Clear();
}

void ImportEditPage::DeleteFromVector(Windows::Foundation::Collections::IObservableVector<hstring> &vector,
                                      const hstring &item)
{
    for (uint32_t i = 0; i < vector.Size(); ++i)
    {
        if (vector.GetAt(i) == item)
        {
            vector.RemoveAt(i);
            return;
        }
    }
}

bool ImportEditPage::FindInVector(Windows::Foundation::Collections::IObservableVector<hstring> &vector,
                                  const hstring &item)
{
    for (uint32_t i = 0; i < vector.Size(); ++i)
    {
        if (vector.GetAt(i) == item)
        {
            return true;
        }
    }
    return false;
}

void ImportEditPage::TrimStringView(std::wstring_view &view)
{
    while (!view.empty() && isspace(view.front()))
    {
        view.remove_prefix(1);
    }
    while (!view.empty() && isspace(view.back()))
    {
        view.remove_suffix(1);
    }
}

void ImportEditPage::AddToActors(const hstring &actor)
{
    std::wstring_view nameView = actor;
    TrimStringView(nameView);
    hstring trimmedName{nameView};
    if (!trimmedName.empty() && !FindInVector(mActors, trimmedName))
    {
        mActors.Append(trimmedName);
    }
    ActorNameInput().Text(L"");
}

void ImportEditPage::AddToTags(const hstring &tag)
{
    std::wstring_view nameView = tag;
    TrimStringView(nameView);
    hstring trimmedName{nameView};
    if (!nameView.empty() && !FindInVector(mTags, trimmedName))
    {
        mTags.Append(trimmedName);
    }
    TagNameInput().Text(L"");
}
} // namespace winrt::vorg_windows::implementation

M vorg-windows/ImportEditPage.xaml.h => vorg-windows/ImportEditPage.xaml.h +3 -31
@@ 15,32 15,13 @@ struct ImportEditPage : ImportEditPageT<ImportEditPage>

    vorg_windows::ImportEditPageState State() const;

    void UserControl_PointerEntered(const Windows::Foundation::IInspectable &sender,
                                    const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &);
    void UserControl_PointerExited(const Windows::Foundation::IInspectable &sender,
                                   const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &);
    void ActorsDeleteButton_Click(const Windows::Foundation::IInspectable &sender,
                                  const Microsoft::UI::Xaml::RoutedEventArgs &e);
    void TagsDeleteButton_Click(const Windows::Foundation::IInspectable &sender,
                                const Microsoft::UI::Xaml::RoutedEventArgs &e);

    void ActorsSubmitButton_Click(const Windows::Foundation::IInspectable &,
                                  const Microsoft::UI::Xaml::RoutedEventArgs &e);
    void TagsSubmitButton_Click(const Windows::Foundation::IInspectable &,
                                const Microsoft::UI::Xaml::RoutedEventArgs &e);

    void ActorNameInput_KeyUp(Windows::Foundation::IInspectable const &,
                              Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const &e);
    void TagNameInput_KeyUp(Windows::Foundation::IInspectable const &,
                            Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const &e);

    void SubmitButton_Click(const Windows::Foundation::IInspectable &, const Microsoft::UI::Xaml::RoutedEventArgs &);

    void StudioInput_TextChanged(const Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
    void VideoDataEditor_StudioTextChanged(const Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
                                 const Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &);
    void ActorNameInput_TextChanged(const Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
    void VideoDataEditor_ActorTextChanged(const Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
                                    const Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &);
    void TagNameInput_TextChanged(const Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
    void VideoDataEditor_TagTextChanged(const Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
                                  const Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &);

    void BackButton_Click(const Windows::Foundation::IInspectable &, const Microsoft::UI::Xaml::RoutedEventArgs &);


@@ 51,8 32,6 @@ struct ImportEditPage : ImportEditPageT<ImportEditPage>
                                                                          Windows::Foundation::IInspectable> &handler);
    void EditFinished(const event_token &token);

    void ClearState();

  private:
    Windows::Foundation::Collections::IObservableVector<hstring> mActors;
    Windows::Foundation::Collections::IObservableVector<hstring> mTags;


@@ 60,13 39,6 @@ struct ImportEditPage : ImportEditPageT<ImportEditPage>
    com_ptr<vorg_windows::implementation::App> mApp;
    event<Windows::Foundation::TypedEventHandler<vorg_windows::ImportEditPage, Windows::Foundation::IInspectable>>
        mEditFinishedEvent;

    static void DeleteFromVector(Windows::Foundation::Collections::IObservableVector<hstring> &vector,
                                 const hstring &item);
    static bool FindInVector(Windows::Foundation::Collections::IObservableVector<hstring> &vector, const hstring &item);
    static void TrimStringView(std::wstring_view &view);
    void AddToActors(const hstring &actor);
    void AddToTags(const hstring &tag);
};
} // namespace winrt::vorg_windows::implementation


M vorg-windows/ImportPage.xaml.cpp => vorg-windows/ImportPage.xaml.cpp +0 -1
@@ 61,7 61,6 @@ void ImportPage::ImportEditPage_EditFinished(const vorg_windows::ImportEditPage 

    // Clear ImportEditPage state
    vorg_windows::ImportEditPage editPage = ContentFrame().Content().as<vorg_windows::ImportEditPage>();
    editPage.ClearState();

    // Check for next file
    hstring filePath = mApp->GetNextFile();

M vorg-windows/VideoDataEditor.idl => vorg-windows/VideoDataEditor.idl +22 -6
@@ 1,11 1,27 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.
import "VideoDataEditorState.idl";

namespace vorg_windows
{
    [default_interface]
    runtimeclass VideoDataEditor : Microsoft.UI.Xaml.Controls.StackPanel
[default_interface] runtimeclass VideoDataEditor : Microsoft.UI.Xaml.Controls.StackPanel
{
    VideoDataEditor();

    VideoDataEditorState State
    {
        VideoDataEditor();
    }
        get;
    };

    event Windows.Foundation.TypedEventHandler<Microsoft.UI.Xaml.Controls.AutoSuggestBox,
                                               Microsoft.UI.Xaml.Controls.AutoSuggestBoxTextChangedEventArgs>
        StudioTextChanged;
    event Windows.Foundation.TypedEventHandler<Microsoft.UI.Xaml.Controls.AutoSuggestBox,
                                               Microsoft.UI.Xaml.Controls.AutoSuggestBoxTextChangedEventArgs>
        ActorTextChanged;
    event Windows.Foundation.TypedEventHandler<Microsoft.UI.Xaml.Controls.AutoSuggestBox,
                                               Microsoft.UI.Xaml.Controls.AutoSuggestBoxTextChangedEventArgs>
        TagTextChanged;

    void AddAllCurrent();
    void ClearState();
}
} // namespace vorg_windows

M vorg-windows/VideoDataEditor.xaml => vorg-windows/VideoDataEditor.xaml +184 -1
@@ 8,4 8,187 @@
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:vorg_windows"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" />
    mc:Ignorable="d">
    <Grid
        Margin="0,16,0,0"
        ColumnSpacing="16"
        RowSpacing="8">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>

        <!--  Title  -->
        <TextBlock Grid.Row="0" Grid.Column="0">Title</TextBlock>
        <TextBox
            x:Name="TitleInput"
            Grid.Row="1"
            Grid.Column="0"
            Text="{x:Bind Path=State.Title, Mode=TwoWay}" />

        <!--  Studio  -->
        <TextBlock Grid.Row="0" Grid.Column="1">Studio</TextBlock>
        <AutoSuggestBox
            x:Name="StudioInput"
            Grid.Row="1"
            Grid.Column="1"
            Text="{x:Bind Path=State.Studio, Mode=TwoWay}" />
    </Grid>

    <Grid
        Margin="0,16,0,0"
        ColumnSpacing="16"
        RowSpacing="8">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>

        <!--  Actors  -->
        <TextBlock Grid.Row="0" Grid.Column="0">Actors</TextBlock>
        <Grid
            Grid.Row="1"
            Grid.Column="0"
            ColumnSpacing="8">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <AutoSuggestBox
                x:Name="ActorNameInput"
                Grid.Column="0"
                HorizontalAlignment="Stretch"
                KeyUp="ActorNameInput_KeyUp" />
            <Button
                x:Name="ActorsSubmitButton"
                Grid.Column="1"
                VerticalAlignment="Stretch"
                Click="ActorsSubmitButton_Click"
                Content="&#xE710;"
                FontFamily="Segoe Fluent Icons"
                IsTabStop="False" />
        </Grid>
        <ListView
            Grid.Row="2"
            Grid.Column="0"
            ItemsSource="{x:Bind Path=State.Actors, Mode=TwoWay}"
            SelectionMode="Single">
            <ItemsControl.ItemTemplate>
                <DataTemplate x:DataType="x:String">
                    <UserControl PointerEntered="UserControl_PointerEntered" PointerExited="UserControl_PointerExited">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <TextBlock
                                Grid.Column="0"
                                VerticalAlignment="Center"
                                Text="{x:Bind}" />
                            <Button
                                x:Name="ActorsDeleteButton"
                                Grid.Column="1"
                                Click="ActorsDeleteButton_Click"
                                Content="&#xE711;"
                                FontFamily="Segoe Fluent Icons"
                                Visibility="Collapsed" />
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="HoverButtonsHidden" />
                                    <VisualState x:Name="HoverButtonsShown">
                                        <VisualState.Setters>
                                            <Setter Target="ActorsDeleteButton.Visibility" Value="Visible" />
                                        </VisualState.Setters>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                        </Grid>
                    </UserControl>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style BasedOn="{StaticResource DefaultListViewItemStyle}" TargetType="ListViewItem">
                    <Setter Property="IsTabStop" Value="False" />
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ListView>

        <!--  Tags  -->
        <TextBlock Grid.Row="0" Grid.Column="1">Tags</TextBlock>
        <Grid
            Grid.Row="1"
            Grid.Column="1"
            ColumnSpacing="8">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <AutoSuggestBox
                x:Name="TagNameInput"
                Grid.Column="0"
                HorizontalAlignment="Stretch"
                KeyUp="TagNameInput_KeyUp" />
            <Button
                x:Name="TagsSubmitButton"
                Grid.Column="1"
                VerticalAlignment="Stretch"
                Click="TagsSubmitButton_Click"
                Content="&#xE710;"
                FontFamily="Segoe Fluent Icons"
                IsTabStop="False" />
        </Grid>
        <ListView
            Grid.Row="2"
            Grid.Column="1"
            ItemsSource="{x:Bind Path=State.Tags, Mode=TwoWay}"
            SelectionMode="Single">
            <ItemsControl.ItemTemplate>
                <DataTemplate x:DataType="x:String">
                    <UserControl PointerEntered="UserControl_PointerEntered" PointerExited="UserControl_PointerExited">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <TextBlock
                                Grid.Column="0"
                                VerticalAlignment="Center"
                                Text="{x:Bind}" />
                            <Button
                                x:Name="TagsDeleteButton"
                                Grid.Column="1"
                                Click="TagsDeleteButton_Click"
                                Content="&#xE711;"
                                FontFamily="Segoe Fluent Icons"
                                Visibility="Collapsed" />
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="HoverButtonsHidden" />
                                    <VisualState x:Name="HoverButtonsShown">
                                        <VisualState.Setters>
                                            <Setter Target="TagsDeleteButton.Visibility" Value="Visible" />
                                        </VisualState.Setters>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                        </Grid>
                    </UserControl>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style BasedOn="{StaticResource DefaultListViewItemStyle}" TargetType="ListViewItem">
                    <Setter Property="IsTabStop" Value="False" />
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ListView>
    </Grid>
</StackPanel>

M vorg-windows/VideoDataEditor.xaml.cpp => vorg-windows/VideoDataEditor.xaml.cpp +187 -8
@@ 1,7 1,5 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

#include "pch.h"

#include "VideoDataEditor.xaml.h"
#if __has_include("VideoDataEditor.g.cpp")
#include "VideoDataEditor.g.cpp"


@@ 10,13 8,194 @@
using namespace winrt;
using namespace Microsoft::UI::Xaml;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace winrt::vorg_windows::implementation
{
    VideoDataEditor::VideoDataEditor()
VideoDataEditor::VideoDataEditor()
{
    InitializeComponent();
}

vorg_windows::VideoDataEditorState VideoDataEditor::State() const
{
    return mState;
}

void VideoDataEditor::ActorNameInput_KeyUp(Windows::Foundation::IInspectable const &,
                                           Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const &e)
{
    if (e.Key() == Windows::System::VirtualKey::Enter)
    {
        AddToActors(ActorNameInput().Text());
    }
}

void VideoDataEditor::TagNameInput_KeyUp(Windows::Foundation::IInspectable const &,
                                         Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const &e)
{
    if (e.Key() == Windows::System::VirtualKey::Enter)
    {
        AddToTags(TagNameInput().Text());
    }
}

void VideoDataEditor::ActorsDeleteButton_Click(const Windows::Foundation::IInspectable &sender,
                                               const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    Microsoft::UI::Xaml::Controls::Button button = sender.as<Microsoft::UI::Xaml::Controls::Button>();
    Windows::Foundation::IInspectable dataContext = button.DataContext();
    hstring itemName = dataContext.as<hstring>();
    DeleteFromVector(mState.Actors(), itemName);
}

void VideoDataEditor::TagsDeleteButton_Click(const Windows::Foundation::IInspectable &sender,
                                             const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    Microsoft::UI::Xaml::Controls::Button button = sender.as<Microsoft::UI::Xaml::Controls::Button>();
    Windows::Foundation::IInspectable dataContext = button.DataContext();
    hstring itemName = dataContext.as<hstring>();
    DeleteFromVector(mState.Tags(), itemName);
}

void VideoDataEditor::ActorsSubmitButton_Click(const Windows::Foundation::IInspectable &,
                                               const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    AddToActors(ActorNameInput().Text());
}

void VideoDataEditor::TagsSubmitButton_Click(const Windows::Foundation::IInspectable &,
                                             const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    AddToTags(TagNameInput().Text());
}

void VideoDataEditor::UserControl_PointerEntered(const Windows::Foundation::IInspectable &sender,
                                                const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &)
{
    Microsoft::UI::Xaml::Controls::UserControl userControl = sender.as<Microsoft::UI::Xaml::Controls::UserControl>();
    VisualStateManager::GoToState(userControl, L"HoverButtonsShown", false);
}

void VideoDataEditor::UserControl_PointerExited(const Windows::Foundation::IInspectable &sender,
                                               const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &)
{
    Microsoft::UI::Xaml::Controls::UserControl userControl = sender.as<Microsoft::UI::Xaml::Controls::UserControl>();
    VisualStateManager::GoToState(userControl, L"HoverButtonsHidden", false);
}

void VideoDataEditor::AddAllCurrent()
{
    AddToActors(ActorNameInput().Text());
    AddToTags(TagNameInput().Text());
}

void VideoDataEditor::ClearState()
{
    TitleInput().Text(L"");
    StudioInput().Text(L"");
    ActorNameInput().Text(L"");
    TagNameInput().Text(L"");
    mState.Actors().Clear();
    mState.Tags().Clear();
}

event_token VideoDataEditor::StudioTextChanged(
    const Windows::Foundation::TypedEventHandler<Microsoft::UI::Xaml::Controls::AutoSuggestBox,
                                                 Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs>
        &handler)
{
    return StudioInput().TextChanged(handler);
}

void VideoDataEditor::StudioTextChanged(const event_token &token)
{
    StudioInput().TextChanged(token);
}

event_token VideoDataEditor::ActorTextChanged(
    const Windows::Foundation::TypedEventHandler<Microsoft::UI::Xaml::Controls::AutoSuggestBox,
                                                 Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs>
        &handler)
{
    return ActorNameInput().TextChanged(handler);
}

void VideoDataEditor::ActorTextChanged(const event_token &token)
{
    ActorNameInput().TextChanged(token);
}

event_token VideoDataEditor::TagTextChanged(
    const Windows::Foundation::TypedEventHandler<Microsoft::UI::Xaml::Controls::AutoSuggestBox,
                                                 Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs>
        &handler)
{
    return TagNameInput().TextChanged(handler);
}

void VideoDataEditor::TagTextChanged(const event_token &token)
{
    TagNameInput().TextChanged(token);
}

void VideoDataEditor::AddToActors(const hstring &actor)
{
    std::wstring_view nameView = actor;
    TrimStringView(nameView);
    hstring trimmedName{nameView};
    if (!trimmedName.empty() && !FindInVector(mState.Actors(), trimmedName))
    {
        mState.Actors().Append(trimmedName);
    }
    ActorNameInput().Text(L"");
}

void VideoDataEditor::AddToTags(const hstring &tag)
{
    std::wstring_view nameView = tag;
    TrimStringView(nameView);
    hstring trimmedName{nameView};
    if (!nameView.empty() && !FindInVector(mState.Actors(), trimmedName))
    {
        mState.Tags().Append(trimmedName);
    }
    TagNameInput().Text(L"");
}

bool VideoDataEditor::FindInVector(Windows::Foundation::Collections::IObservableVector<hstring> vector,
                                   const hstring &item)
{
    for (uint32_t i = 0; i < vector.Size(); ++i)
    {
        if (vector.GetAt(i) == item)
        {
            return true;
        }
    }
    return false;
}

void VideoDataEditor::TrimStringView(std::wstring_view &view)
{
    while (!view.empty() && isspace(view.front()))
    {
        view.remove_prefix(1);
    }
    while (!view.empty() && isspace(view.back()))
    {
        view.remove_suffix(1);
    }
}

void VideoDataEditor::DeleteFromVector(Windows::Foundation::Collections::IObservableVector<hstring> vector,
                                       const hstring &item)
{
    for (uint32_t i = 0; i < vector.Size(); ++i)
    {
        InitializeComponent();
        if (vector.GetAt(i) == item)
        {
            vector.RemoveAt(i);
            return;
        }
    }
}
} // namespace winrt::vorg_windows::implementation

M vorg-windows/VideoDataEditor.xaml.h => vorg-windows/VideoDataEditor.xaml.h +67 -9
@@ 5,17 5,75 @@

#include "VideoDataEditor.g.h"

#include "VideoDataEditorState.h"

namespace winrt::vorg_windows::implementation
{
    struct VideoDataEditor : VideoDataEditorT<VideoDataEditor>
    {
        VideoDataEditor();
    };
}
struct VideoDataEditor : VideoDataEditorT<VideoDataEditor>
{
    VideoDataEditor();

    vorg_windows::VideoDataEditorState State() const;

    void ActorNameInput_KeyUp(Windows::Foundation::IInspectable const &,
                              Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const &e);
    void TagNameInput_KeyUp(Windows::Foundation::IInspectable const &,
                            Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const &e);

    void ActorsDeleteButton_Click(const Windows::Foundation::IInspectable &sender,
                                  const Microsoft::UI::Xaml::RoutedEventArgs &e);
    void TagsDeleteButton_Click(const Windows::Foundation::IInspectable &sender,
                                const Microsoft::UI::Xaml::RoutedEventArgs &e);

    void ActorsSubmitButton_Click(const Windows::Foundation::IInspectable &,
                                  const Microsoft::UI::Xaml::RoutedEventArgs &e);
    void TagsSubmitButton_Click(const Windows::Foundation::IInspectable &,
                                const Microsoft::UI::Xaml::RoutedEventArgs &e);

    void UserControl_PointerEntered(const Windows::Foundation::IInspectable &sender,
                                    const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &);
    void UserControl_PointerExited(const Windows::Foundation::IInspectable &sender,
                                   const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &);

    void AddAllCurrent();

    void ClearState();

    event_token StudioTextChanged(
        const Windows::Foundation::TypedEventHandler<Microsoft::UI::Xaml::Controls::AutoSuggestBox,
                                                     Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs>
            &handler);
    void StudioTextChanged(const event_token &token);

    event_token ActorTextChanged(
        const Windows::Foundation::TypedEventHandler<Microsoft::UI::Xaml::Controls::AutoSuggestBox,
                                                     Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs>
            &handler);
    void ActorTextChanged(const event_token &token);

    event_token TagTextChanged(
        const Windows::Foundation::TypedEventHandler<Microsoft::UI::Xaml::Controls::AutoSuggestBox,
                                                     Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs>
            &handler);
    void TagTextChanged(const event_token &token);

  private:
    // Members
    vorg_windows::VideoDataEditorState mState;

    // Helper functions
    void AddToActors(const hstring &actor);
    void AddToTags(const hstring &tag);
    static void TrimStringView(std::wstring_view &view);
    static bool FindInVector(Windows::Foundation::Collections::IObservableVector<hstring> vector, const hstring &item);
    static void DeleteFromVector(Windows::Foundation::Collections::IObservableVector<hstring> vector,
                                 const hstring &item);
};
} // namespace winrt::vorg_windows::implementation

namespace winrt::vorg_windows::factory_implementation
{
    struct VideoDataEditor : VideoDataEditorT<VideoDataEditor, implementation::VideoDataEditor>
    {
    };
}
struct VideoDataEditor : VideoDataEditorT<VideoDataEditor, implementation::VideoDataEditor>
{
};
} // namespace winrt::vorg_windows::factory_implementation

A vorg-windows/VideoDataEditorState.cpp => vorg-windows/VideoDataEditorState.cpp +68 -0
@@ 0,0 1,68 @@
#include "pch.h"

#include "VideoDataEditorState.h"
#if __has_include("VideoDataEditorState.g.cpp")
#include "VideoDataEditorState.g.cpp"
#endif

namespace winrt::vorg_windows::implementation
{
VideoDataEditorState::VideoDataEditorState()
    : mActors(single_threaded_observable_vector<hstring>()), mTags(single_threaded_observable_vector<hstring>())
{
}

hstring VideoDataEditorState::Title() const
{
    return mTitle;
}

void VideoDataEditorState::Title(const hstring &title)
{
    mTitle = title;
    mPropertyChangedEvent(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{L"Title"});
}

hstring VideoDataEditorState::Studio() const
{
    return mStudio;
}

void VideoDataEditorState::Studio(const hstring &studio)
{
    mStudio = studio;
    mPropertyChangedEvent(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{L"Studio"});
}

Windows::Foundation::Collections::IObservableVector<hstring> VideoDataEditorState::Actors() const
{
    return mActors;
}

void VideoDataEditorState::Actors(const Windows::Foundation::Collections::IObservableVector<hstring> &actors)
{
    mActors = actors;
    mPropertyChangedEvent(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{L"Actors"});
}

Windows::Foundation::Collections::IObservableVector<hstring> VideoDataEditorState::Tags() const
{
    return mTags;
}

void VideoDataEditorState::Tags(const Windows::Foundation::Collections::IObservableVector<hstring> &tags)
{
    mTags = tags;
    mPropertyChangedEvent(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{L"Tags"});
}

event_token VideoDataEditorState::PropertyChanged(const Microsoft::UI::Xaml::Data::PropertyChangedEventHandler &handler)
{
    return mPropertyChangedEvent.add(handler);
}

void VideoDataEditorState::PropertyChanged(const event_token &token)
{
    mPropertyChangedEvent.remove(token);
}
} // namespace winrt::vorg_windows::implementation
\ No newline at end of file

A vorg-windows/VideoDataEditorState.h => vorg-windows/VideoDataEditorState.h +42 -0
@@ 0,0 1,42 @@
#pragma once

#include "VideoDataEditorState.g.h"

namespace winrt::vorg_windows::implementation
{
struct VideoDataEditorState : VideoDataEditorStateT<VideoDataEditorState>
{
    VideoDataEditorState();

    hstring Title() const;
    void Title(const hstring &title);

    hstring Studio() const;
    void Studio(const hstring &studio);

    Windows::Foundation::Collections::IObservableVector<hstring> Actors() const;
    void Actors(const Windows::Foundation::Collections::IObservableVector<hstring> &actors);

    Windows::Foundation::Collections::IObservableVector<hstring> Tags() const;
    void Tags(const Windows::Foundation::Collections::IObservableVector<hstring> &tags);

    event_token PropertyChanged(const Microsoft::UI::Xaml::Data::PropertyChangedEventHandler &handler);
    void PropertyChanged(const event_token &token);

  private:
    hstring mTitle;
    hstring mStudio;

    Windows::Foundation::Collections::IObservableVector<hstring> mActors;
    Windows::Foundation::Collections::IObservableVector<hstring> mTags;

    event<Microsoft::UI::Xaml::Data::PropertyChangedEventHandler> mPropertyChangedEvent;
};
} // namespace winrt::vorg_windows::implementation

namespace winrt::vorg_windows::factory_implementation
{
struct VideoDataEditorState : VideoDataEditorStateT<VideoDataEditorState, implementation::VideoDataEditorState>
{
};
} // namespace winrt::vorg_windows::factory_implementation

A vorg-windows/VideoDataEditorState.idl => vorg-windows/VideoDataEditorState.idl +29 -0
@@ 0,0 1,29 @@
namespace vorg_windows
{
[default_interface] runtimeclass VideoDataEditorState : Microsoft.UI.Xaml.Data.INotifyPropertyChanged
{
    VideoDataEditorState();

    String Title
    {
        get;
        set;
    };
    String Studio
    {
        get;
        set;
    };

    Windows.Foundation.Collections.IObservableVector<String> Actors
    {
        get;
        set;
    };
    Windows.Foundation.Collections.IObservableVector<String> Tags
    {
        get;
        set;
    };
}
} // namespace vorg_windows
\ No newline at end of file

M vorg-windows/vorg-windows.vcxproj => vorg-windows/vorg-windows.vcxproj +3 -0
@@ 168,6 168,7 @@
    <ClInclude Include="MainWindow.xaml.h">
      <DependentUpon>MainWindow.xaml</DependentUpon>
    </ClInclude>
    <ClInclude Include="VideoDataEditorState.h" />
    <ClInclude Include="VideoDataEditor.xaml.h">
      <DependentUpon>VideoDataEditor.xaml</DependentUpon>
      <SubType>Code</SubType>


@@ 256,6 257,7 @@
      <DependentUpon>MainWindow.xaml</DependentUpon>
    </ClCompile>
    <ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
    <ClCompile Include="VideoDataEditorState.cpp" />
    <ClCompile Include="VideoDataEditor.xaml.cpp">
      <DependentUpon>VideoDataEditor.xaml</DependentUpon>
      <SubType>Code</SubType>


@@ 308,6 310,7 @@
      <DependentUpon>ManagePage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Midl>
    <Midl Include="VideoDataEditorState.idl" />
    <Midl Include="VideoDataEditor.idl">
      <DependentUpon>VideoDataEditor.xaml</DependentUpon>
      <SubType>Code</SubType>

M vorg-windows/vorg-windows.vcxproj.filters => vorg-windows/vorg-windows.vcxproj.filters +9 -0
@@ 37,6 37,9 @@
    <Midl Include="ImportEditPageState.idl">
      <Filter>Data Types</Filter>
    </Midl>
    <Midl Include="VideoDataEditorState.idl">
      <Filter>Data Types</Filter>
    </Midl>
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="pch.cpp" />


@@ 47,6 50,9 @@
    <ClCompile Include="ImportEditPageState.cpp">
      <Filter>Data Types</Filter>
    </ClCompile>
    <ClCompile Include="VideoDataEditorState.cpp">
      <Filter>Data Types</Filter>
    </ClCompile>
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="pch.h" />


@@ 56,6 62,9 @@
    <ClInclude Include="ImportEditPageState.h">
      <Filter>Data Types</Filter>
    </ClInclude>
    <ClInclude Include="VideoDataEditorState.h">
      <Filter>Data Types</Filter>
    </ClInclude>
  </ItemGroup>
  <ItemGroup>
    <Image Include="Assets\Wide310x150Logo.scale-200.png">