Skip to content

Commit

Permalink
Merge pull request #825 from storybuilder-org/AddStructure
Browse files Browse the repository at this point in the history
Add structure
  • Loading branch information
terrycox authored Nov 17, 2024
2 parents 25fed42 + 13b24ed commit fe52c4f
Show file tree
Hide file tree
Showing 32 changed files with 1,471 additions and 590 deletions.
4 changes: 4 additions & 0 deletions StoryCAD/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using StoryCAD.Services;
using StoryCAD.Services.Collaborator;
using Microsoft.Extensions.DependencyInjection;
using StoryCAD.Models.Tools;

namespace StoryCAD;

Expand Down Expand Up @@ -75,6 +76,9 @@ public App()

InitializeComponent();

// Make sure ToolsData is loaded by forcing instantiation
serviceProvider.GetRequiredService<ToolsData>();

_log = Ioc.Default.GetService<LogService>();
Current.UnhandledException += OnUnhandledException;
}
Expand Down
1 change: 1 addition & 0 deletions StoryCAD/StoryCAD.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
</PropertyGroup>
<PropertyGroup>
<WindowsSdkPackageVersion>10.0.22621.38</WindowsSdkPackageVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Content Include="Assets\SplashScreen.scale-200.png" />
Expand Down
107 changes: 103 additions & 4 deletions StoryCAD/Views/ProblemPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
x:Class="StoryCAD.Views.ProblemPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:StoryCAD.Views"
xmlns:usercontrols="using:StoryCAD.Controls"
xmlns:viewModels="using:StoryCAD.ViewModels">
xmlns:viewModels="using:StoryCAD.ViewModels"
xmlns:tools="using:StoryCAD.ViewModels.Tools">

<Grid>
<Grid.RowDefinitions>
Expand Down Expand Up @@ -58,7 +58,7 @@
PlaceholderText="{x:Bind ProblemVm.ProblemSource, Mode=TwoWay}" />
</Grid>
</PivotItem>
<PivotItem Header="Protagonist" >
<PivotItem Header="Protagonist">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
Expand Down Expand Up @@ -141,7 +141,106 @@
PlaceholderText="A [character] in a situation [genre, setting] wants something [goal], which brings him into [conflict] with a second character [opposition]. After [a series of conflicts], the [final battle] erupts, and [the protagonist] finally [resolves] the conflict. "/>
</Grid>
</PivotItem>
<PivotItem Header="Notes">
<PivotItem Header="Structure">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<!-- Beat picker and Title/Desc of Overall beat -->
<!--Double bind to fix WinUI 3 binding issue -->
<ComboBox Header="Structure" ItemsSource="{x:Bind BeatSheetsViewModel.PlotPatternNames}"
MinWidth="200" SelectionChanged="{x:Bind ProblemVm.UpdateSelectedBeat}"
PlaceholderText="{x:Bind ProblemVm.StructureModelTitle, Mode=OneWay}"
SelectedValue="{x:Bind ProblemVm.StructureModelTitle, Mode=OneWay}"
Text="{x:Bind ProblemVm.StructureModelTitle, Mode=OneWay}" Grid.Row="0"/>
<TextBlock Text="Structure Description:" Margin="0,10,0,0" Grid.Row="2"/>
<TextBlock Text="{x:Bind ProblemVm.StructureDescription, Mode=TwoWay}" Grid.Row="3"
TextWrapping="Wrap"/>

<!-- Beats viewer -->
<ItemsRepeater ItemsSource="{x:Bind ProblemVm.StructureBeats, Mode=TwoWay}"
HorizontalAlignment="Stretch" Grid.Row="4">
<ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="tools:StructureBeatViewModel">
<Grid DataContext="{x:Bind}" MinWidth="200" AllowDrop="True" DragOver="UIElement_OnDragOver"
Background="Transparent" CanDrag="False" Drop="DroppedItem" Margin="0,5,10,5"
BorderBrush="White" BorderThickness="1" CornerRadius="4"
Padding="5,10,10,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>

<!-- Outer Grid Content -->
<TextBlock Grid.Row="0" Grid.Column="1" Text="{x:Bind Title}"
FontSize="18" TextWrapping="Wrap"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{x:Bind Description}"
TextWrapping="Wrap"/>
<StackPanel Grid.Row="0" Grid.Column="2" Orientation="Horizontal"
DataContext="{x:Bind Mode=TwoWay}">
</StackPanel>

<!-- Inner Grid Content Merged -->
<Border Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Margin="0,10"
BorderBrush="White" BorderThickness="1" CornerRadius="4"/>

<SymbolIcon Grid.Row="3" Grid.Column="0" Symbol="{Binding ElementIcon}"
VerticalAlignment="Bottom"/>

<TextBlock Grid.Row="3" Grid.Column="1" Text="{x:Bind ElementName, Mode=OneWay}"
Margin="10,5,0,0" VerticalAlignment="Top" TextWrapping="Wrap"/>

<usercontrols:RichEditBoxExtended Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="3"
RtfText="{x:Bind ElementDescription, Mode=OneWay}"
Margin="5" IsReadOnly="True"
VerticalAlignment="Top" TextWrapping="Wrap"
TextAlignment="Center"/>
</Grid>

</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</Grid>
</ScrollViewer>

<!-- Scene/Problem picker -->
<Button Content="Show Problems/Scenes" Grid.Column="1" VerticalAlignment="Top">
<Button.Flyout>
<Flyout>
<StackPanel>
<!-- Shows scenes and problems in a list -->
<ListView ItemsSource="{x:Bind ProblemVm.Scenes, Mode=TwoWay}" Width="350" CanDrag="True"
Header="Scenes" DisplayMemberPath="Name" CanDragItems="True"
AllowDrop="True" DragItemsStarting="ListViewBase_OnDragItemsStarting"/>
<ListView ItemsSource="{x:Bind ProblemVm.Problems, Mode=TwoWay}" Width="350" CanDrag="True"
Header="Problems" DisplayMemberPath="Name" CanDragItems="True"
AllowDrop="True" DragItemsStarting="ListViewBase_OnDragItemsStarting"/>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</Grid>
</PivotItem>
<PivotItem Header="Notes">
<usercontrols:RichEditBoxExtended Header="Notes" RtfText="{x:Bind ProblemVm.Notes, Mode=TwoWay}"
AcceptsReturn="True" IsSpellCheckEnabled="True" TextWrapping="Wrap"
ScrollViewer.VerticalScrollBarVisibility="Visible" />
Expand Down
123 changes: 113 additions & 10 deletions StoryCAD/Views/ProblemPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,125 @@
namespace StoryCAD.Views;
using Windows.ApplicationModel.DataTransfer;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using StoryCAD.Services.Logging;
using StoryCAD.ViewModels.Tools;

namespace StoryCAD.Views;

public sealed partial class ProblemPage : BindablePage
{
public ProblemViewModel ProblemVm;
public ShellViewModel ShellVm => Ioc.Default.GetService<ShellViewModel>();


public ProblemPage()
public BeatSheetsViewModel BeatSheetsViewModel = Ioc.Default.GetService<BeatSheetsViewModel>();
public LogService LogService = Ioc.Default.GetService<LogService>();
public ProblemPage()
{
ProblemVm = Ioc.Default.GetService<ProblemViewModel>();
InitializeComponent();
DataContext = ProblemVm;
}

#region DND
/// <summary>
/// Ran when an item is dropped on the right side of the beat panel
/// </summary>
private async void DroppedItem(object sender, DragEventArgs e)
{
var stackPanel = sender as Grid;
if (stackPanel == null) return;
//Extract model
var structureBeatsModel = stackPanel.DataContext as StructureBeatViewModel;
if (structureBeatsModel == null) { return; }

try // Now, you can access structureBeatsModel to know which item was dropped on
{
string text = await e.DataView.GetTextAsync();
//Check we are dragging an element GUID and not something else
if (!text.Contains("GUID")) { return; }

//Parse out the element GUID.
Guid.TryParse(text.Split(":")[1], out Guid uuid);

//Find element being dropped.
StoryElement Element = ShellVm.StoryModel.StoryElements.First(g => g.Uuid == uuid);
int ElementIndex = ShellVm.StoryModel.StoryElements.IndexOf(Element);

//Check if problem is being dropped and enforce rule.
if (Element.Type == StoryItemType.Problem)
{
ProblemModel problem = (ProblemModel)Element;
//Enforce rule that problems can only be bound to one structure beat model
if (!string.IsNullOrEmpty(problem.BoundStructure)) //Check element is actually bound elsewhere
{
ProblemModel ContainingStructure = (ProblemModel)ShellVm.StoryModel.StoryElements.First(g => g.Uuid == Guid.Parse(problem.BoundStructure));
//Show dialog asking to rebind.
var res = await Ioc.Default.GetRequiredService<Windowing>().ShowContentDialog(new()
{
Title = "Already assigned!",
Content = $"This problem is already assigned to a different structure ({ContainingStructure.Name}) " +
$"Would you like to assign it here instead?",
PrimaryButtonText = "Assign here",
SecondaryButtonText = "Cancel"
});

//Do nothing if user clicks don't rebind.
if (res != ContentDialogResult.Primary) { return; }

if (problem.BoundStructure.Equals(ProblemVm.Uuid.ToString())) //Rebind from VM
{
StructureBeatViewModel oldStructure = ContainingStructure.StructureBeats.First(g => g.Guid == problem.Uuid.ToString());
int index = ProblemVm.StructureBeats.IndexOf(oldStructure);
ProblemVm.StructureBeats[index].Guid = String.Empty;
}
else //Remove from old structure and update story elements.
{
StructureBeatViewModel oldStructure = ContainingStructure.StructureBeats.First(g => g.Guid == problem.Uuid.ToString());
int index = ContainingStructure.StructureBeats.IndexOf(oldStructure);
ContainingStructure.StructureBeats[index].Guid = String.Empty;
int ContainingStructIndex = ShellVm.StoryModel.StoryElements.IndexOf(ContainingStructure);
ShellVm.StoryModel.StoryElements[ContainingStructIndex] = ContainingStructure;
}
}

if (problem.Uuid == ProblemVm.Uuid)
{
ProblemVm.BoundStructure = ProblemVm.Uuid.ToString();
}
else
{
problem.BoundStructure = ProblemVm.Uuid.ToString();
ShellVm.StoryModel.StoryElements[ElementIndex] = problem;
}
}

structureBeatsModel.Guid = uuid.ToString();
stackPanel.DataContext = structureBeatsModel;
}
catch (Exception ex)
{
LogService.Log(LogLevel.Warn, $"Failed to drag valid element (StructureDND)" +
$" (This is expected if non element object was DND) {ex.Message}");
}
}

private void UIElement_OnDragOver(object sender, DragEventArgs e)
{
e.AcceptedOperation = DataPackageOperation.Move;
e.Handled = true;
}

//private void Conflict_ContextRequested(UIElement sender, ContextRequestedEventArgs args)
//{
// FlyoutShowOptions myOption = new();
// myOption.ShowMode = FlyoutShowMode.Transient;
// ConflictCommandBarFlyout.ShowAt(NavigationTree, myOption);
//}
private void ListViewBase_OnDragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
if (e.Items.Count > 0)
{
var draggedStoryElement = e.Items[0] as StoryElement; // Cast to StoryElement
if (draggedStoryElement != null)
{
// Set the StoryElement object as part of the drag data
e.Data.SetText("GUID:" + draggedStoryElement.Uuid.ToString());
e.Data.RequestedOperation = DataPackageOperation.Move;
}
}
}
#endregion
}
2 changes: 1 addition & 1 deletion StoryCAD/Views/Shell.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ private void TreeViewItem_DragLeave(object sender, DragEventArgs e)

Logger.Log(LogLevel.Trace, $"DragLeave entry");
Logger.Log(LogLevel.Trace, $"DragLeave exit");
}
}

/// <summary>
/// Handles the DragItemsCompleted event for TreeViewItem. This event will complete the drag and drop
Expand Down
Loading

0 comments on commit fe52c4f

Please sign in to comment.