<RootNamespace>Crow.Test</RootNamespace>
<AssemblyName>Crow.Test</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+ <ReleaseVersion>0.5</ReleaseVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<EmbeddedResource Include="Templates\DockingView.template">
<LogicalName>Crow.DockingView.template</LogicalName>
</EmbeddedResource>
+ <EmbeddedResource Include="Templates\FileItems.template" />
</ItemGroup>
<ItemGroup>
<None Include="Crow.dll.config">
</p>
</h1>
+## Presentation
**C.R.O.W.** is a [widget toolkit](https://en.wikipedia.org/wiki/Widget_toolkit) and
rendering engine entirely developed in **C#**, offering a nice trade-off between
complexity of language and performances. Crow provides a declarative interface language
</a>
</p>
-For **documentation** and **tutorials** visit the [Wiki](https://github.com/jpbruyere/Crow/wiki)
-or the [Project Site](https://jpbruyere.github.io/Crow/).
+#### Features
+- [Declarative interface definition](interface-markup-language).
+- [Templates](Templates)
+- [Styling](Styling)
+- [Dynamic binding system](The-binding-system)
+- SVG rendering (with [rsvg library](https://developer.gnome.org/rsvg/))
+
+#### Documentation
+* [Introduction](Global-architecture)
+* [Classes documentation autogenerated from doxygen](index)
+* [Tutorials](Tutorials)
Please report bugs and issues on [GitHub](https://github.com/jpbruyere/Crow/issues)
## Getting Start
### Requirements
-- [mono > 5.0](http://www.mono-project.com/download/)
-- [Cairo Graphic Library](https://cairographics.org/) >= 1.10
+- [mono >= 5.0](http://www.mono-project.com/download/)
+- [Cairo Graphic Library](https://cairographics.org/) >= 1.20
- [rsvg library](https://developer.gnome.org/rsvg/) for svg rendering
- [nuget](https://www.nuget.org/).
<DirectoryView ShowFiles="true" Name="fv" CurrentDirectory="{./CurrentDirectory}" SelectedItemChanged="./onFVSelectedItemChanged"
Width="100%" Margin="0" MouseDoubleClick="./onFileSelect">
<Template>
- <ListBox Name="fileView" Data="{./FileSystemEntries}" SelectedItemChanged="./onSelectedItemChanged">
+ <ListBox ItemTemplate="#Crow.Templates.FileItems.template" Name="fileView" Data="{./FileSystemEntries}"
+ SelectedItemChanged="./onSelectedItemChanged">
<Template>
<HorizontalStack>
<Scroller ScrollX="{../scrollbar1.Value}" Name="scroller1"
Width="14" />
</HorizontalStack>
</Template>
- <ItemTemplate DataType="System.IO.FileInfo">
- <HorizontalStack Focusable="true" Height="Fit">
- <Image Margin="2" Width="16" Height="16"
- Path="#Crow.Icons.file.svg"/>
- <Label Text="{Name}" Width="Stretched"
- MouseEnter="{Background=BlueCrayola}"
- MouseLeave="{Background=Transparent}"/>
- </HorizontalStack>
- </ItemTemplate>
- <ItemTemplate DataType="System.IO.DirectoryInfo">
- <Border Foreground="Transparent" Focusable="true" Height="Fit">
- <HorizontalStack
- MouseEnter="{Background=BlueCrayola}"
- MouseLeave="{Background=Transparent}">
- <Image Margin="2" Width="16" Height="16"
- Path="#Crow.Icons.folder.svg"/>
- <Label Text="{Name}" Width="Stretched"/>
- <Label Text="{LastAccessTime}" />
- </HorizontalStack>
- </Border>
- </ItemTemplate>
</ListBox>
</Template>
</DirectoryView>
--- /dev/null
+<ItemTemplate>
+ <GraphicObject Height="16" Background="Red"/>
+</ItemTemplate>
+<ItemTemplate DataType="System.IO.FileInfo">
+ <HorizontalStack Focusable="true" Height="Fit">
+ <Image Margin="2" Width="16" Height="16"
+ Path="#Crow.Icons.file.svg"/>
+ <Label Text="{Name}" Width="Stretched"
+ MouseEnter="{Background=BlueCrayola}"
+ MouseLeave="{Background=Transparent}"/>
+ </HorizontalStack>
+</ItemTemplate>
+<ItemTemplate DataType="System.IO.DirectoryInfo">
+ <Border Foreground="Transparent" Focusable="true" Height="Fit">
+ <HorizontalStack
+ MouseEnter="{Background=BlueCrayola}"
+ MouseLeave="{Background=Transparent}">
+ <Image Margin="2" Width="16" Height="16"
+ Path="#Crow.Icons.folder.svg"/>
+ <Label Text="{Name}" Width="Stretched"/>
+ <Label Text="{LastAccessTime}" />
+ </HorizontalStack>
+ </Border>
+</ItemTemplate>
+
public event EventHandler Pressed;
public event EventHandler Released;
- #region TemplatedContainer overrides
- public override GraphicObject Content {
- get {
- return _contentContainer == null ? null : _contentContainer.Child;
- }
- set {
- if (_contentContainer != null)
- _contentContainer.SetChild(value);
- }
- }
- protected override void loadTemplate(GraphicObject template = null)
- {
- base.loadTemplate (template);
-
- _contentContainer = this.child.FindByName ("Content") as Container;
- }
- #endregion
-
#region GraphicObject Overrides
public override void onMouseDown (object sender, MouseButtonEventArgs e)
{
{
IsExpanded = !IsExpanded;
}
- /// <summary>
- /// Implement the abstract Content property of TemplatedControl
- /// </summary>
- public override GraphicObject Content {
- get {
- return _contentContainer == null ? null : _contentContainer.Child;
- }
- set {
- _contentContainer.SetChild(value);
- NotifyValueChanged ("HasContent", HasContent);
- }
- }
- //TODO: move loadTemplate and ResolveBinding in TemplatedContainer
- protected override void loadTemplate(GraphicObject template = null)
- {
- base.loadTemplate (template);
-
- _contentContainer = this.child.FindByName ("Content") as Container;
- }
#region Public properties
[XmlAttributeAttribute][DefaultValue("#Crow.Images.Icons.expandable.svg")]
onCollapse (this, null);
}
}
- [XmlIgnore]public bool HasContent {
- get { return _contentContainer == null ? false : _contentContainer.Child != null; }
- }
[XmlIgnore]public bool IsExpandable {
get {
try {
public Rectangle LastSlots;
/// <summary>
/// keep last slot painted on screen to clear traces if moved or resized
- /// TODO: we should ensure the whole parsed widget tree is the last painted
/// version to clear effective oldslot if parents have been moved or resized.
/// IDEA is to add a ScreenCoordinates function that use only lastPaintedSlots
/// </summary>
+ //TODO: we should ensure the whole parsed widget tree is the last painted
public Rectangle LastPaintedSlot;
/// <summary>Prevent requeuing multiple times the same widget</summary>
public bool IsQueueForRedraw = false;
public GroupBox () : base(){}
public GroupBox(Interface iface) : base(iface){}
#endregion
-
- #region TemplatedContainer implementation
- public override GraphicObject Content {
- get {
- return _contentContainer == null ? null : _contentContainer.Child;
- }
- set {
- _contentContainer.SetChild(value);
- }
- }
- protected override void loadTemplate(GraphicObject template = null)
- {
- base.loadTemplate (template);
-
- _contentContainer = this.child.FindByName ("Content") as Container;
- }
- #endregion
}
}
bool _isPopped, _canPop;
Alignment popDirection;
- GraphicObject _contentContainer;
+ GraphicObject _content;
Measure popWidth, popHeight;
public event EventHandler Popped;
#endregion
public override GraphicObject Content {
- get { return _contentContainer; }
+ get { return _content; }
set {
- if (_contentContainer != null) {
- _contentContainer.LogicalParent = null;
- _contentContainer.LayoutChanged -= _content_LayoutChanged;
+ if (_content != null) {
+ _content.LogicalParent = null;
+ _content.LayoutChanged -= _content_LayoutChanged;
}
- _contentContainer = value;
+ _content = value;
- if (_contentContainer == null)
+ if (_content == null)
return;
- _contentContainer.LogicalParent = this;
- _contentContainer.HorizontalAlignment = HorizontalAlignment.Left;
- _contentContainer.VerticalAlignment = VerticalAlignment.Top;
- _contentContainer.LayoutChanged += _content_LayoutChanged;
+ _content.LogicalParent = this;
+ _content.HorizontalAlignment = HorizontalAlignment.Left;
+ _content.VerticalAlignment = VerticalAlignment.Top;
+ _content.LayoutChanged += _content_LayoutChanged;
}
}
void positionContent(LayoutingType lt){
protected override void Dispose (bool disposing)
{
- if (_contentContainer != null && disposing) {
- if (_contentContainer.Parent == null)
- _contentContainer.Dispose ();
+ if (_content != null && disposing) {
+ if (_content.Parent == null)
+ _content.Dispose ();
}
base.Dispose (disposing);
}
{
base.loadTemplate (template);
- _contentContainer = this.child.FindByName ("Content") as Container;
titleWidget = this.child.FindByName ("TabTitle");
}
internal GraphicObject TabTitle { get { return titleWidget; }}
namespace Crow
{
- public abstract class TemplatedContainer : TemplatedControl
+ /// <summary>
+ /// base class for new containers that will use templates.
+ ///
+ /// TemplatedControl's **must** provide a widget of the [`Container`](Container) class named **_'Content'_** inside their template tree
+ /// </summary>
+ public class TemplatedContainer : TemplatedControl
{
#region CTOR
public TemplatedContainer() : base(){}
protected Container _contentContainer;
- [XmlAttributeAttribute]public virtual GraphicObject Content{ get; set;}
+ /// <summary>
+ /// Single child of this templated container.
+ /// </summary>
+ public virtual GraphicObject Content {
+ get {
+ return _contentContainer == null ? null : _contentContainer.Child;
+ }
+ set {
+ _contentContainer.SetChild(value);
+ NotifyValueChanged ("HasContent", HasContent);
+ }
+ }
+ [XmlIgnore]public bool HasContent {
+ get { return _contentContainer?.Child != null; }
+ }
+ //TODO: move loadTemplate and ResolveBinding in TemplatedContainer
+ protected override void loadTemplate(GraphicObject template = null)
+ {
+ base.loadTemplate (template);
+ _contentContainer = this.child.FindByName ("Content") as Container;
+ }
#region GraphicObject overrides
public override GraphicObject FindByName (string nameToFind)
/// <summary>
/// Template path
/// </summary>
+ //TODO: this property should be renamed 'TemplatePath'
[XmlAttributeAttribute][DefaultValue(null)]
public string Template {
get { return _template; }
}
}
/// <summary>
- /// caption property being recurrent in templated widget, it is declared here.
+ /// a caption being recurrent need in templated widget, it is declared here.
/// </summary>
[XmlAttributeAttribute()][DefaultValue("Templated Control")]
public virtual string Caption {
public Dictionary<string, ItemTemplate> ItemTemplates = new Dictionary<string, Crow.ItemTemplate>();
/// <summary>
- /// Default item template
+ /// Item templates file path, on disk or embedded.
+ ///
+ /// ItemTemplate file may contains either a single template without the
+ /// ItemTemplate enclosing tag, or several item templates each enclosed
+ /// in a separate tag
/// </summary>
[XmlAttributeAttribute][DefaultValue("#Crow.Templates.ItemTemplate.goml")]
public string ItemTemplate {
/// Items loading thread
/// </summary>
void loading(){
- if (ItemTemplates == null)
- ItemTemplates = new Dictionary<string, ItemTemplate> ();
- if (!ItemTemplates.ContainsKey ("default"))
- ItemTemplates ["default"] = Interface.GetItemTemplate (ItemTemplate);
+ //if (!ItemTemplates.ContainsKey ("default"))
+ // ItemTemplates ["default"] = Interface.GetItemTemplate (ItemTemplate);
for (int i = 1; i <= (data.Count / itemPerPage) + 1; i++) {
if ((bool)loadingThread?.cancelRequested) {
#endregion
#region TemplatedContainer overrides
- public override GraphicObject Content {
- get { return _contentContainer == null ? null : _contentContainer.Child; }
- set { _contentContainer.SetChild(value); }
- }
protected override void loadTemplate(GraphicObject template = null)
{
base.loadTemplate (template);
- _contentContainer = this.child.FindByName ("Content") as Container;
NotifyValueChanged ("ShowNormal", false);
NotifyValueChanged ("ShowMinimize", true);
Stopwatch loadingTime = new Stopwatch ();
loadingTime.Start ();
#endif
- using (XmlTextReader itr = new XmlTextReader (stream)) {
+ using (XmlReader itr = XmlReader.Create (stream)) {
parseIML (itr);
}
+ stream.Dispose ();
#if DEBUG_LOAD
loadingTime.Stop ();
Debug.WriteLine ("IML Instantiator creation '{2}' : {0} ticks, {1} ms",
loadingTime.ElapsedTicks, loadingTime.ElapsedMilliseconds, imlPath);
#endif
}
+ /// <summary>
+ /// Initializes a new instance of the Instantiator class with an already openned xml reader
+ /// positionned on the start tag inside the itemTemplate
+ /// </summary>
+ public Instantiator (XmlReader itr){
+ parseIML (itr);
+ }
//TODO:check if still used
public Instantiator (Type _root, InstanciatorInvoker _loader)
{
/// <summary>
/// Parses IML and build a dynamic method that will be used to instanciate one or multiple occurence of the IML file or fragment
/// </summary>
- void parseIML (XmlTextReader reader) {
+ void parseIML (XmlReader reader) {
IMLContext ctx = new IMLContext (findRootType (reader));
ctx.nodesStack.Push (new Node (ctx.RootType));
/// read first node to set GraphicObject class for loading
/// and let reader position on that node
/// </summary>
- Type findRootType (XmlTextReader reader)
+ Type findRootType (XmlReader reader)
{
string root = "Object";
- while (reader.Read ()) {
- if (reader.NodeType == XmlNodeType.Element) {
- root = reader.Name;
- break;
- }
- }
+ while (reader.NodeType != XmlNodeType.Element)
+ reader.Read ();
+ root = reader.Name;
Type t = tryGetGOType (root);
if (t == null)
throw new Exception ("IML parsing error: undefined root type (" + root + ")");
return t;
}
- void emitLoader (XmlTextReader reader, IMLContext ctx)
+ /// <summary>
+ /// main parsing entry point
+ /// </summary>
+ void emitLoader (XmlReader reader, IMLContext ctx)
{
string tmpXml = reader.ReadOuterXml ();
//emitCheckAndBindValueChanged (ctx);
}
/// <summary>
+ /// Parses the item template tag.
+ /// </summary>
+ /// <returns>the string triplet dataType, itemTmpID read as attribute of this tag</returns>
+ /// <param name="reader">current xml text reader</param>
+ /// /// <param name="itemTemplatePath">file containing the templates if its a dedicated one</param>
+ string[] parseItemTemplateTag (XmlReader reader, string itemTemplatePath = "") {
+ string dataType = "default", datas = "", path = "";
+ while (reader.MoveToNextAttribute ()) {
+ if (reader.Name == "DataType")
+ dataType = reader.Value;
+ else if (reader.Name == "Data")
+ datas = reader.Value;
+ else if (reader.Name == "Path")
+ path = reader.Value;
+ }
+ reader.MoveToElement ();
+
+ string itemTmpID = itemTemplatePath;
+
+ if (string.IsNullOrEmpty (path)) {
+ itemTmpID += Guid.NewGuid ().ToString ();
+ Interface.Instantiators [itemTmpID] =
+ new ItemTemplate (new MemoryStream (Encoding.UTF8.GetBytes (reader.ReadInnerXml ())), dataType, datas);
+
+ } else {
+ if (!reader.IsEmptyElement)
+ throw new Exception ("ItemTemplate with Path attribute set may not include sub nodes");
+ itemTmpID += path+dataType+datas;
+ if (!Interface.Instantiators.ContainsKey (itemTmpID))
+ Interface.Instantiators [itemTmpID] =
+ new ItemTemplate (Interface.GetStreamFromPath (path), dataType, datas);
+ }
+ return new string [] { dataType, itemTmpID, datas };
+ }
+ /// <summary>
/// process template and item template definition prior to
/// other attributes or childs processing
/// </summary>
reader.Read ();
string templatePath = reader.GetAttribute ("Template");
+ string itemTemplatePath = reader.GetAttribute ("ItemTemplate");
int depth = reader.Depth + 1;
while (reader.Read ()) {
inlineTemplate = true;
reader.Read ();
readChildren (reader, ctx, -1);
- } else if (reader.Name == "ItemTemplate") {
- string dataType = "default", datas = "", path = "";
- while (reader.MoveToNextAttribute ()) {
- if (reader.Name == "DataType")
- dataType = reader.Value;
- else if (reader.Name == "Data")
- datas = reader.Value;
- else if (reader.Name == "Path")
- path = reader.Value;
- }
- reader.MoveToElement ();
-
- string itemTmpID;
-
- if (string.IsNullOrEmpty (path)) {
- itemTmpID = Guid.NewGuid ().ToString ();
- Interface.Instantiators [itemTmpID] =
- new ItemTemplate (new MemoryStream (Encoding.UTF8.GetBytes (reader.ReadInnerXml ())), dataType, datas);
-
- } else {
- if (!reader.IsEmptyElement)
- throw new Exception ("ItemTemplate with Path attribute may not include sub nodes");
- itemTmpID = path+dataType+datas;
- if (!Interface.Instantiators.ContainsKey (itemTmpID))
- Interface.Instantiators [itemTmpID] =
- new ItemTemplate (Interface.GetStreamFromPath (itemTmpID), dataType, datas);
- }
- itemTemplateIds.Add (new string [] { dataType, itemTmpID, datas });
- }
+ } else if (reader.Name == "ItemTemplate")
+ itemTemplateIds.Add (parseItemTemplateTag (reader));
}
if (!inlineTemplate) {//load from path or default template
}
ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miLoadTmp);//load template
}
+ if (itemTemplateIds.Count == 0) {
+ //try to load ItemTemplate(s) from ItemTemplate attribute of TemplatedGroup
+ if (!string.IsNullOrEmpty (itemTemplatePath)) {
+ //check if it is already loaded in cache as a single itemTemplate instantiator
+ if (Interface.Instantiators.ContainsKey (itemTemplatePath)) {
+ itemTemplateIds.Add (new string [] { "default", itemTemplatePath, "" });
+ } else {
+ using (Stream stream = Interface.GetStreamFromPath (itemTemplatePath)) {
+ //itemtemplate files may have multiple root nodes
+ XmlReaderSettings itrSettings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment };
+ using (XmlReader itr = XmlReader.Create (stream, itrSettings)) {
+ while (itr.Read ()) {
+ if (!itr.IsStartElement ())
+ continue;
+ if (itr.NodeType == XmlNodeType.Element) {
+ if (itr.Name != "ItemTemplate") {
+ //the file contains a single template to use as default
+ Interface.Instantiators [itemTemplatePath] =
+ new ItemTemplate (itr);
+ itemTemplateIds.Add (new string [] { "default", itemTemplatePath, "" });
+ break;//we should be at the end of the file
+ }
+ itemTemplateIds.Add (parseItemTemplateTag (itr, itemTemplatePath));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
//copy item templates (review this)
foreach (string [] iTempId in itemTemplateIds) {
ctx.il.Emit (OpCodes.Ldloc_0);//load TempControl ref
strDataType = _dataType;
fetchMethodName = _fetchDataMethod;
}
+ public ItemTemplate (XmlReader reader, string _dataType = null, string _fetchDataMethod = null)
+ :base(reader)
+ {
+ strDataType = _dataType;
+ fetchMethodName = _fetchDataMethod;
+ }
#endregion
public void CreateExpandDelegate (TemplatedGroup host){