1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 using System.Collections;
  8 using System.Windows;
  9 using System.Windows.API;
 10 
 11 namespace System.Windows
 12 {
 13     public nothrow System.Windows.Color DefaultIconListViewTextColor()
 14     {
 15         return System.Windows.Color.Black();
 16     }
 17     
 18     public nothrow System.Windows.Color DefaultIconListViewIconSelectedColor()
 19     {
 20         return System.Windows.Color(64u96u156u210u);
 21     }
 22 
 23     public nothrow Padding DefaultIconListViewIconPadding()
 24     {
 25         return Padding(8888);
 26     }
 27 
 28     public nothrow Padding DefaultIconListViewTextPadding()
 29     {
 30         return Padding(4444);
 31     }
 32 
 33     public nothrow Padding DefaultIconListViewInternalPadding()
 34     {
 35         return Padding(4444);
 36     }
 37 
 38     public nothrow string DefaultIconListViewFontFamilyName()
 39     {
 40         return "Segoe UI";
 41     }
 42 
 43     public nothrow float DefaultIconListViewFontSize()
 44     {
 45         return 9.0f;
 46     }
 47 
 48     public class delegate void IconListViewSelectedIndexChangedEventHandler();
 49     public class delegate void IconListViewIconDoubleClickedEventHandler();
 50 
 51     public class IconListViewItem : Component
 52     {
 53         public enum Flags : sbyte
 54         {
 55             none = 0selected = 1 << 0
 56         }
 57         public nothrow IconListViewItem(const string& iconName_const string& itemName_) : index(-1)iconName(iconName_)itemName(itemName_)location()size()
 58         {
 59         }
 60         public void Measure(Graphics& graphicsconst Padding& iconPaddingconst Padding& textPaddingconst Font& font)
 61         {
 62             iconSize = Size(cast<int>(bitmap->GetWidth())cast<int>(bitmap->GetHeight()));
 63             PointF origin;
 64             StringFormat format(StringAlignment.nearStringAlignment.near);
 65             RectF textRect = graphics.MeasureStringChecked(itemNamefontoriginformat);
 66             textSize = textRect.size;
 67             size.w = Max(cast<int>(iconSize.w + iconPadding.Horizontal())cast<int>(textSize.w + textPadding.Horizontal()));
 68             size.h = cast<int>(iconSize.h + iconPadding.Vertical() + textSize.h + textPadding.Vertical());
 69         }
 70         public void SetLocations(const Point& locconst Padding& iconPaddingconst Padding& textPadding)
 71         {
 72             location = loc;
 73             if (textSize.w > iconSize.w)
 74             {
 75                 iconLocation = Point(cast<int>(location.x + iconPadding.left + ((textSize.w + textPadding.Horizontal()) - (iconSize.w + iconPadding.Horizontal())) / 2)location.y + iconPadding.top);
 76                 textLocation = PointF(location.x + textPadding.leftlocation.y + (size.h - textSize.h - textPadding.Vertical()) + textPadding.top);
 77             }
 78             else
 79             {
 80                 iconLocation = Point(location.x + iconPadding.leftlocation.y + iconPadding.top);
 81                 textLocation = PointF(location.x + textPadding.left + ((iconSize.w + iconPadding.Horizontal()) - (textSize.w + textPadding.Horizontal())) / 2location.y + (size.h - textSize.h - textPadding.Vertical()) + textPadding.top);
 82             }
 83         }
 84         public void Draw(Graphics& graphicsIconListView& iconListView)
 85         {
 86             if (IsSelected())
 87             {
 88                 SolidBrush& selectedBrush = iconListView.GetSelectedBrush();
 89                 RectF rect(PointF(location.xlocation.y)SizeF(size.wsize.h));
 90                 graphics.FillRectangleChecked(selectedBrushrect);
 91             }
 92             Rect r(iconLocationiconSize);
 93             graphics.DrawImageChecked(*bitmapr00iconSize.wiconSize.hUnit.pixeliconListView.GetImageAttributes());
 94             RectF textRect(textLocationtextSize);
 95             graphics.DrawStringChecked(itemNameiconListView.GetFont()textRecticonListView.GetStringFormat()iconListView.GetTextBrush());
 96         }
 97         public nothrow void SetSelected()
 98         {
 99             flags = cast<Flags>(flags | Flags.selected);
100         }
101         public nothrow void ResetSelected()
102         {
103             flags = cast<Flags>(flags & ~Flags.selected);
104         }
105         public nothrow bool IsSelected() const
106         {
107             return (flags & Flags.selected) != Flags.none;
108         }
109         private Flags flags;
110         public int index;
111         public string iconName;
112         public string itemName;
113         public Point location;
114         public Size size;
115         public Point iconLocation;
116         public Size iconSize;
117         public PointF textLocation;
118         public SizeF textSize;
119         public Bitmap* bitmap;
120     }
121 
122     public nothrow ControlCreateParams& IconListViewControlCreateParams(ControlCreateParams& controlCreateParams)
123     {
124         controlCreateParams.SetWindowClassName("System.Windows.IconListView");
125         controlCreateParams.SetWindowClassStyle(DoubleClickWindowClassStyle());
126         controlCreateParams.SetWindowClassBackgroundColor(SystemColor.COLOR_WINDOW);
127         controlCreateParams.SetBackgroundColor(Color.White());
128         return controlCreateParams;
129     }
130     
131     public class IconListViewCreateParams
132     {
133         public nothrow IconListViewCreateParams(ControlCreateParams& controlCreateParams_) : 
134             controlCreateParams(controlCreateParams_)
135             fontFamilyName(DefaultIconListViewFontFamilyName())
136             fontSize(DefaultIconListViewFontSize())
137             textColor(DefaultIconListViewTextColor())
138             iconSelectedColor(DefaultIconListViewIconSelectedColor())
139             bitmapTransparentColor(System.Windows.Color.DefaultBitmapTransparent())
140             iconPadding(DefaultIconListViewIconPadding())
141             textPadding(DefaultIconListViewTextPadding())
142             internalPadding(DefaultIconListViewInternalPadding())
143         {
144         }
145         public IconListViewCreateParams& SetFontFamilyName(const string& fontFamilyName_)
146         {
147             fontFamilyName = fontFamilyName_;
148             return *this;
149         }
150         public IconListViewCreateParams& SetFontSize(float fontSize_)
151         {
152             fontSize = fontSize_;
153             return *this;
154         }
155         public IconListViewCreateParams& SetTextColor(const Color& textColor_)
156         {
157             textColor = textColor_;
158             return *this;
159         }
160         public IconListViewCreateParams& SetIconSelectedColor(const Color& iconSelectedColor_)
161         {
162             iconSelectedColor = iconSelectedColor_;
163             return *this;
164         }
165         public IconListViewCreateParams& SetIconPadding(const Padding& iconPadding_)
166         {
167             iconPadding = iconPadding_;
168             return *this;
169         }
170         public IconListViewCreateParams& SetTextPadding(const Padding& textPadding_)
171         {
172             textPadding = textPadding_;
173             return *this;
174         }
175         public IconListViewCreateParams& SetInternalPadding(const Padding& internalPadding_)
176         {
177             internalPadding = internalPadding_;
178             return *this;
179         }
180         public ControlCreateParams& controlCreateParams;
181         public string fontFamilyName;
182         public float fontSize;
183         public Color textColor;
184         public Color iconSelectedColor;
185         public Color bitmapTransparentColor;
186         public Padding iconPadding;
187         public Padding textPadding;
188         public Padding internalPadding;
189     }
190     
191     public class IconListView : Control
192     {
193         private enum Flags : sbyte
194         {
195             none = 0changed = 1 << 0
196         }
197 
198         public IconListView(const System.Windows.Color& backgroundColorconst Point& locationconst Size& sizeDock dockAnchors anchors) : 
199             base("System.Windows.IconListView"DoubleClickWindowClassStyle()DefaultChildWindowStyle()DefaultExtendedWindowStyle()
200             backgroundColor"iconListView"locationsizedockanchors)items(this)numberOfItems(0)
201             fontFamily(DefaultIconListViewFontFamilyName())
202             fontSize(DefaultIconListViewFontSize())
203             font(fontFamilyfontSize)
204             bitmapTransparentColor(System.Windows.Color.DefaultBitmapTransparent())
205             iconSelectedColor(DefaultIconListViewIconSelectedColor())
206             textColor(DefaultIconListViewTextColor())
207             iconPadding(DefaultIconListViewIconPadding())
208             textPadding(DefaultIconListViewTextPadding())
209             internalPadding(DefaultIconListViewInternalPadding())
210             imageAttributes()stringFormat(StringAlignment.centerStringAlignment.center)selectedItem(null)
211         {
212             imageAttributes.SetColorKeyChecked(bitmapTransparentColorbitmapTransparentColorColorAdjustType.default_);
213             SetChanged();
214         }
215         public IconListView(const Point& locationconst Size& sizeDock dockAnchors anchors) : 
216             this(System.Windows.Color.White()locationsizedockanchors)
217         {
218         }
219         public IconListView(IconListViewCreateParams& createParams) : 
220             base(createParams.controlCreateParams)
221             items(this)numberOfItems(0)
222             fontFamily(createParams.fontFamilyName)
223             fontSize(createParams.fontSize)
224             font(fontFamilyfontSize)
225             bitmapTransparentColor(createParams.bitmapTransparentColor)
226             iconSelectedColor(createParams.iconSelectedColor)
227             textColor(createParams.textColor)
228             iconPadding(createParams.iconPadding)
229             textPadding(createParams.textPadding)
230             internalPadding(createParams.internalPadding)
231             imageAttributes()stringFormat(StringAlignment.centerStringAlignment.center)selectedItem(null)
232         {
233             imageAttributes.SetColorKeyChecked(bitmapTransparentColorbitmapTransparentColorColorAdjustType.default_);
234             SetChanged();
235         }
236         public void AddItem(const string& iconNameconst string& itemName)
237         {
238             UniquePtr<IconListViewItem> item(new IconListViewItem(iconNameitemName));
239             item->index = numberOfItems;
240             item->bitmap = GetOrInsertBitmap(iconName);
241             items.AddChild(item.Release());
242             ++numberOfItems;
243             SetChanged();
244             Invalidate();
245         }
246         protected override void OnMouseDown(MouseEventArgs& args)
247         {
248             if (selectedItem != null)
249             {
250                 selectedItem->ResetSelected();
251             }
252             Point loc = args.location;
253             Component* component = items.FirstChild();
254             while (component != null)
255             {
256                 if (component is IconListViewItem*)
257                 {
258                     IconListViewItem* item = cast<IconListViewItem*>(component);
259                     Rect rect(item->locationitem->size);
260                     if (rect.Contains(loc))
261                     {
262                         item->SetSelected();
263                         selectedItem = item;
264                         OnSelectedIndexChanged();
265                         break;
266                     }
267                 }
268                 component = component->NextSibling();
269             }
270             Invalidate();
271         }
272         protected override void OnMouseDoubleClick(MouseEventArgs& args)
273         {
274             Point loc = args.location;
275             Component* component = items.FirstChild();
276             while (component != null)
277             {
278                 if (component is IconListViewItem*)
279                 {
280                     IconListViewItem* item = cast<IconListViewItem*>(component);
281                     Rect rect(item->locationitem->size);
282                     if (rect.Contains(loc))
283                     {
284                         item->SetSelected();
285                         selectedItem = item;
286                         OnDoubleClicked();
287                         break;
288                     }
289                 }
290                 component = component->NextSibling();
291             }
292             Invalidate();
293         }
294         public nothrow void SetBitmapTransparentColor(const System.Windows.Color& bitmapTransparentColor_)
295         {
296             bitmapTransparentColor = bitmapTransparentColor_;
297             imageAttributes.SetColorKeyChecked(bitmapTransparentColorbitmapTransparentColorColorAdjustType.default_);
298         }
299         public nothrow void SetIconSelectedColor(const System.Windows.Color& iconSelectedColor_)
300         {
301             iconSelectedColor = iconSelectedColor_;
302         }
303         public nothrow void SetTextColor(const System.Windows.Color& textColor_)
304         {
305             textColor = textColor_;
306         }
307         public nothrow void SetIconPadding(const Padding& iconPadding_)
308         {
309             iconPadding = iconPadding_;
310         }
311         public nothrow void SetTextPadding(const Padding& textPadding_)
312         {
313             textPadding = textPadding_;
314         }
315         public nothrow void SetInternalPadding(const Padding& internalPadding_)
316         {
317             internalPadding = internalPadding_;
318         }
319         public void SetFont(const Font& font_)
320         {
321             font = font_;
322             SetChanged();
323         }
324         internal nothrow const Font& GetFont() const
325         {
326             return font;
327         }
328         internal nothrow const StringFormat& GetStringFormat() const
329         {
330             return stringFormat;
331         }
332         public nothrow Event<IconListViewSelectedIndexChangedEventHandler>& SelectedIndexChangedEvent()
333         {
334             return selectedIndexChangedEvent;
335         }
336         public nothrow Event<IconListViewIconDoubleClickedEventHandler>& IconDoubleClickedEvent()
337         {
338             return iconDoubleClickedEvent;
339         }
340         protected virtual void OnSelectedIndexChanged()
341         {
342             selectedIndexChangedEvent.Fire();
343         }
344         protected virtual void OnDoubleClicked()
345         {
346             iconDoubleClickedEvent.Fire();
347         }
348         public nothrow int GetSelectedIndex() const
349         {
350             if (selectedItem != null)
351             {
352                 return selectedItem->index;
353             }
354             else
355             {
356                 return -1;
357             }
358         }
359         protected override void OnPaint(PaintEventArgs& args)
360         {
361             try
362             {
363                 if (Changed())
364                 {
365                     ResetChanged();
366                     Measure(args.graphics);
367                 }
368                 args.graphics.Clear(BackgroundColor());
369                 Component* component = items.FirstChild();
370                 while (component != null)
371                 {
372                     if (component is IconListViewItem*)
373                     {
374                         IconListViewItem* item = cast<IconListViewItem*>(component);
375                         item->Draw(args.graphics*this);
376                     }
377                     component = component->NextSibling();
378                 }
379             }
380             catch (const Exception& ex)
381             {
382                 MessageBox.Show(ex.Message());
383             }
384             base->OnPaint(args);
385         }
386         private void Measure(Graphics& graphics)
387         {
388             Point loc(internalPadding.leftinternalPadding.top);
389             Size size;
390             Component* component = items.FirstChild();
391             while (component != null)
392             {
393                 if (component is IconListViewItem*)
394                 {
395                     IconListViewItem* item = cast<IconListViewItem*>(component);
396                     item->Measure(graphicsiconPaddingtextPaddingfont);
397                     item->SetLocations(lociconPaddingtextPadding);
398                     loc.x = loc.x + item->size.w;
399                     size.w = size.w + item->size.w;
400                     size.h = Max(size.hitem->size.h);
401                 }
402                 component = component->NextSibling();
403             }
404             size.w = size.w + internalPadding.Horizontal();
405             size.h = size.h + internalPadding.Vertical();
406             SetSize(size);
407         }
408         internal ImageAttributes& GetImageAttributes()
409         {
410             return imageAttributes;
411         }
412         internal SolidBrush& GetTextBrush()
413         {
414             return *GetOrInsertSolidBrush(textColor);
415         }
416         internal SolidBrush& GetSelectedBrush()
417         {
418             return *GetOrInsertSolidBrush(iconSelectedColor);
419         }
420         private Bitmap* GetOrInsertBitmap(const string& bitmapName)
421         {
422             try
423             {
424                 HashMap<stringBitmap*>.ConstIterator it = bitmapMap.CFind(bitmapName);
425                 if (it != bitmapMap.CEnd())
426                 {
427                     return it->second;
428                 }
429                 UniquePtr<Bitmap> bitmapPtr(new Bitmap(Bitmap.FromResource(bitmapName)));
430                 Bitmap* bitmap = bitmapPtr.Get();
431                 bitmapMap[bitmapName] = bitmap;
432                 bitmaps.Add(Rvalue(bitmapPtr));
433                 return bitmap;
434             }
435             catch (const Exception& ex)
436             {
437                 throw Exception("could not load bitmap '" + bitmapName + "': " + ex.Message());
438             }
439             return null;
440         }
441         private SolidBrush* GetOrInsertSolidBrush(const System.Windows.Color& color)
442         {
443             HashMap<System.Windows.ColorSolidBrush*>.ConstIterator it = brushMap.CFind(color);
444             if (it != brushMap.CEnd())
445             {
446                 return it->second;
447             }
448             else
449             {
450                 UniquePtr<SolidBrush> solidBrushPtr(new SolidBrush(color));
451                 SolidBrush* solidBrush = solidBrushPtr.Get();
452                 brushMap[color] = solidBrush;
453                 brushes.Add(Rvalue(solidBrushPtr));
454                 return solidBrush;
455             }
456         }
457         private nothrow void SetChanged()
458         {
459             flags = cast<Flags>(flags | Flags.changed);
460         }
461         private nothrow void ResetChanged()
462         {
463             flags = cast<Flags>(flags & ~Flags.changed);
464         }
465         private nothrow bool Changed() const
466         {
467             return (flags & Flags.changed) != Flags.none;
468         }
469         private Flags flags;
470         private Container items;
471         private int numberOfItems;
472         private FontFamily fontFamily;
473         private float fontSize;
474         private Font font;
475         private List<UniquePtr<Bitmap>> bitmaps;
476         private HashMap<stringBitmap*> bitmapMap;
477         private System.Windows.Color bitmapTransparentColor;
478         private System.Windows.Color iconSelectedColor;
479         private System.Windows.Color textColor;
480         private List<UniquePtr<SolidBrush>> brushes;
481         private HashMap<System.Windows.ColorSolidBrush*> brushMap;
482         private Padding iconPadding;
483         private Padding textPadding;
484         private Padding internalPadding;
485         private ImageAttributes imageAttributes;
486         private StringFormat stringFormat;
487         private IconListViewItem* selectedItem;
488         private Event<IconListViewSelectedIndexChangedEventHandler> selectedIndexChangedEvent;
489         private Event<IconListViewIconDoubleClickedEventHandler> iconDoubleClickedEvent;
490     }
491 }