Replies: 7 comments 6 replies
-
You're right. At the moment I have created 4 new labels: critical (mostly crashes or really blocker issues), high (something not working at all and has a high impact), medium (something does not work and is considerably important) and low (aesthetic details, format in code or errors at the sample level). I think what you mention is important (high performance is one of the keys of this library) and I think it would be a good first contribution. |
Beta Was this translation helpful? Give feedback.
-
Here's the basic concept that I just coded up for "DrawMapper"... the idea is to change the interface into something that simply returns an IEnumerable<> of LayerDrawCommands. So I've added "GetDrawActions()" to DrawMapper. private readonly LayerDrawAction[] s_NullList = new LayerDrawAction[0];
public virtual IEnumerable<LayerDrawAction> GetDrawActions(string[] layers)
{
if (layers == null)
return s_NullList;
LayerDrawAction[] layerDrawActions;
if (!_drawActionCache.TryGetValue(layers, out layerDrawActions))
{
List<LayerDrawAction> actions = new List<LayerDrawAction>(layers.Length);
foreach (var layer in layers)
{
var action = Get(layer);
if (action != null)
{
LayerDrawAction entry = new LayerDrawAction() { LayerName = layer, DrawAction = action };
actions.Add(entry);
}
}
layerDrawActions = actions.ToArray();
_drawActionCache[layers] = layerDrawActions;
}
return layerDrawActions;
}
private Dictionary<string[], LayerDrawAction[]> _drawActionCache = new Dictionary<string[], LayerDrawAction[]>();
public class LayerDrawAction
{
public string LayerName { get; init; }
public Action<ICanvas, RectangleF, IViewDrawable, IView> DrawAction { get; init; }
} The for GraphicsControlHandler, the "Draw()" method code now looks like this: public virtual void Draw(ICanvas canvas, RectangleF dirtyRect)
{
if (VirtualView == null || _drawMapper == null)
return;
canvas.SaveState();
if (_layerDrawActions == null)
_layerDrawActions = _drawMapper.GetDrawActions(LayerDrawingOrder());
foreach (var action in _layerDrawActions)
{
action.DrawAction.Invoke(canvas, dirtyRect, Drawable, VirtualView);
}
canvas.ResetState();
}
protected IEnumerable<DrawMapper.LayerDrawAction> _layerDrawActions; Currently, if for some reason the Layers Order ever changes, you just need to set "_layerDrawActions = null" and it'll re-figure the list of DrawActions (once more). A similar thing needs to be done for "MixedGraphicsControlHandlers"... Which means I'd need to refactor the "LayerDrawAction" concept to probably be a more generic ICommand interface of some sort. So you just call "Execute(args)" or something. I'm out of time for the night, but just felt like dabbling. |
Beta Was this translation helpful? Give feedback.
-
Question, why would you go through the process of new'ing a List<>, adding items, then causing another allocation for ToArray(), then throwing away the original List<>? Your goal should be to eliminate as many allocations as possible if you're going for the best perf. Just new the array straight away into s_DrawActionCache based on the layer size and place the items directly into it. |
Beta Was this translation helpful? Give feedback.
-
Also a concern here is the Error/Warning/Info Logging that will be used with this library. For example, if you are running with the DEBUG version of this DLL, I would like it to spit out it's logic to a log file with 1000's of info's, alongside warnings/errors. Thus code that does Null-Checking, instead of just returning, could Log.Warning("Draw() called with null Virtual view."). Or use Assert(VirtualView != null, "msg...") which only runs in DEBUG mode. And to make the Release mode DLL run faster, these null-checks could be omitted entirely, as it would simply throw an exception. The methods that "call Draw on Children" would have a try/catch -- which then logs an Error ( The current method, "hides the issue", which can be even more confusing to a developer -- "hey, why isn't control X showing?" |
Beta Was this translation helpful? Give feedback.
-
Can you add a new Category? I'd like to build momentum on these discussions, and inspire more energy/contributions from those listening. Right now the audience is small -- but those listening, probably, like us, really care about this library. === Thus I would suggest that you make use of the existing C# technique called "Collection Initializer" syntax, which allows you to populate Children of a List with "Children = { item1, item2, item3 }" notation. So for example, your method "CreateTimePicker()" would then look like this: IView CreateTimePicker()
{
return CreateContainer("TimePicker", new StackLayout
{
Margin = new Thickness(12, 6, 12, 24),
Children =
{
new Label { FontSize = 9, TextColor = Colors.Gray, Text = "Default" },
new TimePicker(),
new Label { FontSize = 9, TextColor = Colors.Gray, Text = "Disabled" },
new TimePicker { IsEnabled = false },
new Label { FontSize = 9, TextColor = Colors.Gray, Text = "Custom" },
new TimePicker { BackgroundColor = Colors.LightSkyBlue, TextColor = Colors.White }
}
});
} For easy reference, this code CURRENTLY looks like this: IView CreateTimePicker()
{
var layout = new StackLayout
{
Margin = new Thickness(12, 6, 12, 24)
};
layout.Children.Add(new Label { FontSize = 9, TextColor = Colors.Gray, Text = "Default" });
layout.Children.Add(new TimePicker());
layout.Children.Add(new Label { FontSize = 9, TextColor = Colors.Gray, Text = "Disabled" });
layout.Children.Add(new TimePicker { IsEnabled = false });
layout.Children.Add(new Label { FontSize = 9, TextColor = Colors.Gray, Text = "Custom" });
layout.Children.Add(new TimePicker { BackgroundColor = Colors.LightSkyBlue, TextColor = Colors.White });
return CreateContainer("TimePicker", layout);
} |
Beta Was this translation helpful? Give feedback.
-
QUESTION: How do you plan to implement themes/skinning such as "Light" vs "Dark" colors? This too should be part of the sample Gallery app. |
Beta Was this translation helpful? Give feedback.
-
Another Question: Also, have you envisioned if you are going to allow this overall Style to be changed run-time from within the app? (like a Hot-Reload, but doesn't require Visual Studio) Avalonia has this capability in their ControlCatalog, via a combo-box. |
Beta Was this translation helpful? Give feedback.
-
As collaboration begins to help complete the GraphicsControls, we should have a new category for these discussions.
Here's a starting point. I am reviewing some of the existing code, and found this inefficiency at the heart of it.
DrawMapper.DrawLayer (is called for each layer, for each control, for each Draw cycle -- so many times). This method calls:
which is a Dictionary Look-up to find the correct Draw Action to invoke.
Since for any given control type, the List-of-Layers is a fixed-order list, the Actions should also be contained in an Array. If the Order ever changes, then an event can fire to re-bulid this Action-List. So these Dictionary lookups should only be called One-Time for each control type, rather than being called without restraint as they are now.
When it comes to a Library such as "Graphics.Controls", it's vital that they are "efficient" so as to leave more CPU cycles for the applications using them. I'd be happy to write this fix for you now, and it can be my first collaboration.
Beta Was this translation helpful? Give feedback.
All reactions