Skip to content

Commit

Permalink
add Fragment property for #77
Browse files Browse the repository at this point in the history
  • Loading branch information
whistyun committed Nov 17, 2023
1 parent c101963 commit 9a62b1c
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 25 deletions.
5 changes: 5 additions & 0 deletions MdXaml/LinkActions/DiaplayCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public void Execute(object? parameter)
var path = parameter?.ToString();
if (path is null) throw new ArgumentNullException(nameof(parameter));

if (path.StartsWith("file:///"))
{
path = path.Replace('\\', '/');
}

var isAbs = Uri.IsWellFormedUriString(path, UriKind.Absolute);

if (OpenBrowserWithAbsolutePath & isAbs)
Expand Down
17 changes: 1 addition & 16 deletions MdXaml/LinkActions/FlowDocumentJumpAnchorIfNecessary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,7 @@ public void Execute(object? parameter)
{
if (parameter is string linkText && linkText.StartsWith("#"))
{
var identifier = linkText.Substring(1);
if (_viewer.Document is null)
{
Debug.Print($"MarkdownScrollViewer is uninitialized.");
return;
}

var anchor = DocumentAnchor.FindAnchor(_viewer.Document, identifier);

if (anchor is null)
{
Debug.Print($"Not found linkanchor: {identifier}");
return;
}

anchor.BringIntoView();
_viewer.ScrollTo(linkText, true);
}
else _command.Execute(parameter);
}
Expand Down
156 changes: 148 additions & 8 deletions MdXaml/MarkdownScrollViewer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@

using MdXaml.LinkActions;
using MdStyle = MdXaml.MarkdownStyle;
using System.ComponentModel;
using System.Windows.Media.Media3D;
using System.Windows.Threading;
using System.Threading.Tasks;

namespace MdXaml
{
// Markdownを表示するためのControl
[ContentProperty(nameof(HereMarkdown))]
public class MarkdownScrollViewer : FlowDocumentScrollViewer, IUriContext
{
Expand All @@ -29,6 +34,14 @@ public class MarkdownScrollViewer : FlowDocumentScrollViewer, IUriContext
typeof(MarkdownScrollViewer),
new PropertyMetadata(null, UpdateSource));

public static readonly DependencyProperty FragmentProperty =
DependencyProperty.Register(
nameof(Fragment),
typeof(string),
typeof(MarkdownScrollViewer),
new PropertyMetadata(null, UpdateFragment));


public static readonly DependencyProperty MarkdownProperty =
DependencyProperty.Register(
nameof(Markdown),
Expand Down Expand Up @@ -63,7 +76,27 @@ private static void UpdateSource(DependencyObject d, DependencyPropertyChangedEv
{
if (d is MarkdownScrollViewer owner && e.NewValue is Uri source)
{
owner.Open(source, false);
if (owner._source != source)
{
owner._source = source;
owner.Open(source, false);
}
else if (owner.Fragment != source.Fragment)
{
owner.SetCurrentValue(FragmentProperty, source.Fragment);
}
}
}

private static void UpdateFragment(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is MarkdownScrollViewer owner && e.NewValue is string fragment)
{
if (owner._fragment != fragment)
{
owner._fragment = fragment;
owner.ScrollTo(fragment, false);
}
}
}

Expand Down Expand Up @@ -122,14 +155,17 @@ private static void UpdateAssetPathRoot(DependencyObject d, DependencyPropertyCh
if (d is MarkdownScrollViewer owner)
{
var newPath = (string)e.NewValue;
var shouldUpdateMd = newPath != owner.Engine.AssetPathRoot;

owner.Engine.AssetPathRoot = (string)e.NewValue;

if (shouldUpdateMd) UpdateMarkdown(d, e);
if (newPath != owner.Engine.AssetPathRoot)
{
owner.Engine.AssetPathRoot = newPath;
UpdateMarkdown(d, e);
}
}
}

private string _fragment;
private Uri _source;

private Markdown _engine;
public Markdown Engine
{
Expand Down Expand Up @@ -275,6 +311,12 @@ public Uri? Source
set { SetValue(SourceProperty, value); }
}

public string Fragment
{
get { return (string)GetValue(FragmentProperty); }
set { SetValue(FragmentProperty, value); }
}

private ClickAction _clickAction;
public ClickAction ClickAction
{
Expand Down Expand Up @@ -331,6 +373,17 @@ public MarkdownScrollViewer()
menu.Items.Add(ApplicationCommands.SelectAll);

ContextMenu = menu;


DependencyPropertyDescriptor
.FromProperty(FlowDocumentScrollViewer.DocumentProperty, typeof(FlowDocumentScrollViewer))
.AddValueChanged(this, OnDocumentChanged);
}

private void OnDocumentChanged(object sender, EventArgs handler)
{
if (Document is not null)
ScrollTo(Fragment, false);
}

private void UpdateClickAction()
Expand Down Expand Up @@ -365,6 +418,65 @@ private void UpdateClickAction()
Engine.HyperlinkCommand = new FlowDocumentJumpAnchorIfNecessary(this, command);
}

internal void ScrollTo(string fragment, bool updateSourceProperty)
{
if (updateSourceProperty)
{
_fragment = fragment;
SetCurrentValue(FragmentProperty, fragment);
}


if (String.IsNullOrEmpty(fragment))
return;

if (Document is null)
{
Debug.Print($"MarkdownScrollViewer is uninitialized.");
return;
}

var identifier = fragment.StartsWith("#") ?
fragment.Substring(1) :
fragment;

var anchor = DocumentAnchor.FindAnchor(Document, identifier);
if (anchor is null)
{
Debug.Print($"Not found linkanchor: {identifier}");
return;
}

if (anchor.IsLoaded)
{
/*
* dirty hack
*
* I have no idea to detect a text element position in ScrollViewer.
* BringIntoView has no effect if text element is placed in Viewport,
* so scroll to the top once.
*/
var scroll = GetScrollViewer();
scroll?.ScrollToTop();
Dispatcher.Invoke(() => anchor.BringIntoView(), DispatcherPriority.Render);
}
else
anchor.Loaded += (s, e) =>
{
/*
* dirty hack
*
* BringIntoView fails to scroll at correct position when Loaded only.
*/
Dispatcher.Invoke(async () =>
{
await Task.Delay(100);
Dispatcher.Invoke(anchor.BringIntoView, DispatcherPriority.Background);
}, DispatcherPriority.Background);
};

}

internal void Open(Uri source, bool updateSourceProperty)
{
bool TryOpen(Uri path)
Expand Down Expand Up @@ -401,13 +513,20 @@ bool TryOpen(Uri path)

var assetPathRoot = path.Scheme == "file" ? Path.GetDirectoryName(path.LocalPath) : path.AbsoluteUri;

// suppress property changed
Engine.AssetPathRoot = assetPathRoot;

SetCurrentValue(AssetPathRootProperty, assetPathRoot);
SetCurrentValue(MarkdownProperty, newMdTxt);

_fragment = path.Fragment;
SetCurrentValue(FragmentProperty, path.Fragment);

if (updateSourceProperty)
{
_source = path;
SetCurrentValue(SourceProperty, path);
}

SetCurrentValue(MarkdownProperty, newMdTxt);

return true;
}
Expand Down Expand Up @@ -443,6 +562,27 @@ bool TryOpen(Uri path)
Debug.WriteLine($"Failed to open markdown from relative path '{source}': not found");
}
}

private ScrollViewer GetScrollViewer()
{
ScrollViewer Find(Visual visual)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
var child = VisualTreeHelper.GetChild(visual, i);

if (child is ScrollViewer scroll)
return scroll;

if (child is Visual vis)
return Find(vis);
}

return null;
}

return Find(this);
}
}

public enum ClickAction
Expand Down
1 change: 0 additions & 1 deletion samples/MdXaml.Demo2/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public Uri MdSource
get => _MdSource;
set
{
if (_MdSource == value) return;
_MdSource = value;

Histories.Add(value);
Expand Down

0 comments on commit 9a62b1c

Please sign in to comment.