OSDN Git Service

ラベルの再描画を減らす
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / View / ShipLabel.cs
index c834d6a..bec8f9a 100644 (file)
@@ -24,273 +24,353 @@ using static System.Math;
 namespace KancolleSniffer.View\r
 {\r
     [DesignerCategory("Code")]\r
-    public class ShipLabel : Label\r
+    public abstract class ShipLabel : GrowLeftLabel\r
     {\r
-        public static Color[] ColumnColors = {SystemColors.Control, Color.FromArgb(255, 250, 250, 250)};\r
-        public static SizeF ScaleFactor { get; set; }\r
-        public static Font LatinFont { get; set; } = new Font("Tahoma", 8f);\r
-        public Color PresetColor { get; set; }\r
-        public bool AnchorRight { get; set; }\r
-        private int _right = Int32.MinValue;\r
-        private int _left;\r
-        private SlotStatus _slotStatus;\r
-        private ShipStatus _status;\r
-        private bool _hpPercent;\r
+        protected Color InitialBackColor;\r
 \r
         public override Color BackColor\r
         {\r
             get => base.BackColor;\r
-            set => base.BackColor = value == DefaultBackColor ? PresetColor : value;\r
-        }\r
-\r
-        [Flags]\r
-        private enum SlotStatus\r
-        {\r
-            Equipped = 0,\r
-            SemiEquipped = 1,\r
-            NormalEmpty = 2,\r
-            ExtraEmpty = 4\r
+            set\r
+            {\r
+                if (InitialBackColor == Color.Empty)\r
+                    InitialBackColor = value;\r
+                base.BackColor = value;\r
+            }\r
         }\r
 \r
-        public ShipLabel()\r
+        protected ShipLabel()\r
         {\r
             UseMnemonic = false;\r
         }\r
 \r
-        public void SetName(ShipStatus status, ShipNameWidth width = ShipNameWidth.Max)\r
+        public abstract void Set(ShipStatus status);\r
+\r
+        public abstract void Reset();\r
+\r
+        public new sealed class Name : ShipLabel\r
         {\r
-            if (status == null)\r
+            private SlotStatus _slotStatus, _prevSlotStatus;\r
+            private readonly ShipNameWidth _defaultWidth;\r
+\r
+            public static Font LatinFont { get; set; }\r
+            public static Font BaseFont { get; set; }\r
+\r
+            public Name(Point location, ShipNameWidth defaultWidth)\r
+            {\r
+                _defaultWidth = defaultWidth;\r
+                Location = location;\r
+                AutoSize = true;\r
+            }\r
+\r
+            [Flags]\r
+            private enum SlotStatus\r
+            {\r
+                Equipped = 0,\r
+                SemiEquipped = 1,\r
+                NormalEmpty = 2,\r
+                ExtraEmpty = 4\r
+            }\r
+\r
+            public override void Set(ShipStatus status)\r
+            {\r
+                SetName(status, _defaultWidth);\r
+            }\r
+\r
+            public override void Reset()\r
             {\r
                 SetName("");\r
-                return;\r
             }\r
-            var empty = SlotStatus.Equipped;\r
-            if (!status.Empty)\r
+\r
+            public void SetName(ShipStatus status, ShipNameWidth width)\r
+            {\r
+                var slotStatus = GetSlotStatus(status);\r
+                var dcName = DameConName(status);\r
+                var sp = SpecialAttack(status);\r
+                SetName((status.Escaped ? "[避]" : dcName) + sp, status.Name, slotStatus, width);\r
+            }\r
+\r
+            private static SlotStatus GetSlotStatus(ShipStatus status)\r
             {\r
+                if (status.Empty)\r
+                    return SlotStatus.Equipped;\r
                 var slots = status.Slot.Take(status.Spec.SlotNum).ToArray();\r
-                if (slots.All(item => item.Empty))\r
+                var normal = slots.Any(item => item.Empty)\r
+                    ? slots.All(item => item.Empty) ? SlotStatus.NormalEmpty : SlotStatus.SemiEquipped\r
+                    : SlotStatus.Equipped;\r
+                var extra = status.SlotEx.Empty ? SlotStatus.ExtraEmpty : SlotStatus.Equipped;\r
+                return normal | extra;\r
+            }\r
+\r
+            private string DameConName(ShipStatus status)\r
+            {\r
+                switch (status.PreparedDamageControl)\r
                 {\r
-                    empty |= SlotStatus.NormalEmpty;\r
+                    case 42:\r
+                        return "[ダ]";\r
+                    case 43:\r
+                        return "[メ]";\r
+                    default:\r
+                        return "";\r
                 }\r
-                else if (slots.Any(item => item.Empty))\r
+            }\r
+\r
+            private string SpecialAttack(ShipStatus status)\r
+            {\r
+                switch (status.SpecialAttack)\r
                 {\r
-                    empty |= SlotStatus.SemiEquipped;\r
+                    case ShipStatus.Attack.Fire:\r
+                        return "+";\r
+                    case ShipStatus.Attack.Fired:\r
+                        return "-";\r
+                    default:\r
+                        return "";\r
                 }\r
-                if (status.SlotEx.Empty)\r
-                    empty |= SlotStatus.ExtraEmpty;\r
-            }\r
-            var dc = status.PreparedDamageControl;\r
-            var dcName = dc == 42 ? "[ダ]" :\r
-                dc == 43 ? "[メ]" : "";\r
-            var sp = "";\r
-            switch (status.SpecialAttack)\r
-            {\r
-                case ShipStatus.Attack.Fire:\r
-                    sp = "+";\r
-                    break;\r
-                case ShipStatus.Attack.Fired:\r
-                    sp = "-";\r
-                    break;\r
-            }\r
-            SetName((status.Escaped ? "[避]" : dcName) + sp, status.Name, empty, width);\r
-        }\r
+            }\r
 \r
-        public void SetName(string name)\r
-        {\r
-            SetName("", name, SlotStatus.Equipped);\r
-        }\r
+            public void SetName(string name)\r
+            {\r
+                SetName(name, _defaultWidth);\r
+            }\r
 \r
-        public void SetName(string name, ShipNameWidth width)\r
-        {\r
-            SetName("", name, SlotStatus.Equipped, width);\r
-        }\r
+            public void SetName(string name, ShipNameWidth width)\r
+            {\r
+                SetName("", name, SlotStatus.Equipped, width);\r
+            }\r
 \r
-        private void SetName(string prefix, string name, SlotStatus slotStatus, ShipNameWidth width = ShipNameWidth.Max)\r
-        {\r
-            if (name == null)\r
-                name = "";\r
-            _slotStatus = slotStatus;\r
-            var lu = new Regex(@"^\p{Lu}").IsMatch(name);\r
-            var shift = (int)Round(ScaleFactor.Height);\r
-            if (lu && Font.Equals(Parent.Font))\r
+            private void SetName(string prefix, string name, SlotStatus slotStatus, ShipNameWidth width)\r
             {\r
-                Location += new Size(0, -shift);\r
-                Font = LatinFont;\r
+                _slotStatus = slotStatus;\r
+                ChangeFont(name);\r
+                var realWidth = width == ShipNameWidth.Max ? (int)width : Scaler.ScaleWidth((int)width);\r
+                Text = prefix + StringTruncator.Truncate(name, "", realWidth, Font);\r
+                if (_prevSlotStatus != _slotStatus)\r
+                    Invalidate(); // OnPaintを実行させるため\r
+                _prevSlotStatus = _slotStatus;\r
             }\r
-            else if (!lu && !Font.Equals(Parent.Font))\r
+\r
+            private void ChangeFont(string name)\r
             {\r
-                Location += new Size(0, shift);\r
-                Font = Parent.Font;\r
+                var lu = StartWithLetter(name);\r
+                var shift = Scaler.ScaleHeight(1);\r
+                if (lu && !Font.Equals(LatinFont))\r
+                {\r
+                    Location += new Size(0, -shift);\r
+                    Font = LatinFont;\r
+                }\r
+                else if (!lu && Font.Equals(LatinFont))\r
+                {\r
+                    Location += new Size(0, shift);\r
+                    Font = BaseFont;\r
+                }\r
             }\r
-            var result = prefix + name;\r
-            var measured = TextRenderer.MeasureText(result, Font).Width;\r
-            if (measured <= (int)width)\r
+\r
+            public static bool StartWithLetter(string name)\r
             {\r
-                Text = result;\r
-                Invalidate(); // 必ずOnPaintを実行させるため\r
-                return;\r
+                return Regex.IsMatch(name, @"^\p{Lu}");\r
             }\r
-            var truncated = "";\r
-            foreach (var ch in name)\r
+\r
+            protected override void OnPaint(PaintEventArgs e)\r
             {\r
-                var tmp = truncated + ch;\r
-                if (TextRenderer.MeasureText(tmp, Font).Width > (int)width * ScaleFactor.Width)\r
-                    break;\r
-                truncated = tmp;\r
+                base.OnPaint(e);\r
+                if ((_slotStatus & SlotStatus.NormalEmpty) != 0)\r
+                {\r
+                    e.Graphics.DrawRectangle(Pens.Black,\r
+                        new Rectangle(Scaler.Move(ClientSize.Width, 0, -3, 0), Scaler.Scale(2, 5)));\r
+                }\r
+                else if ((_slotStatus & SlotStatus.SemiEquipped) != 0)\r
+                {\r
+                    e.Graphics.DrawLine(Pens.Black,\r
+                        Scaler.Move(ClientSize.Width, 0, -1, 0),\r
+                        Scaler.Move(ClientSize.Width, 0, -1, 5));\r
+                }\r
+                if ((_slotStatus & SlotStatus.ExtraEmpty) != 0)\r
+                {\r
+                    e.Graphics.DrawRectangle(Pens.Black,\r
+                        new Rectangle(Scaler.Move(ClientSize.Width, 0, -3, 8), Scaler.Scale(2, 3)));\r
+                }\r
             }\r
-            Text = prefix + truncated.TrimEnd(' ');\r
-            Invalidate();\r
         }\r
 \r
-        public void SetHp(ShipStatus status)\r
+        public sealed class Hp : ShipLabel\r
         {\r
-            _status = status;\r
-            if (status == null)\r
+            private bool _hpPercent;\r
+            private ShipStatus _status;\r
+\r
+            public Hp()\r
             {\r
+            }\r
+\r
+            public Hp(Point location, int height)\r
+            {\r
+                Location = location;\r
+                MinimumSize = new Size(0, height);\r
+                TextAlign = ContentAlignment.MiddleLeft;\r
+                GrowLeft = true;\r
+                Cursor = Cursors.Hand;\r
+            }\r
+\r
+            public override void Reset()\r
+            {\r
+                _status = null;\r
                 Text = "";\r
-                BackColor = PresetColor;\r
-                return;\r
+                BackColor = InitialBackColor;\r
             }\r
-            Text = _hpPercent\r
-                ? $"{(int)Floor(status.NowHp * 100.0 / status.MaxHp):D}%"\r
-                : $"{status.NowHp:D}/{status.MaxHp:D}";\r
-            BackColor = DamageColor(status, PresetColor);\r
-        }\r
 \r
-        public void ToggleHpPercent()\r
-        {\r
-            _hpPercent = !_hpPercent;\r
-            SetHp(_status);\r
-        }\r
+            public override void Set(ShipStatus status)\r
+            {\r
+                _status = status;\r
+                Text = _hpPercent\r
+                    ? $"{(int)Floor(status.NowHp * 100.0 / status.MaxHp):D}%"\r
+                    : $"{status.NowHp:D}/{status.MaxHp:D}";\r
+                BackColor = DamageColor(status);\r
+            }\r
 \r
-        public void SetHp(int now, int max)\r
-        {\r
-            SetHp(new ShipStatus {NowHp = now, MaxHp = max});\r
-        }\r
+            public void ToggleHpPercent()\r
+            {\r
+                _hpPercent = !_hpPercent;\r
+                if (_status != null)\r
+                    Set(_status);\r
+            }\r
 \r
-        public static Color DamageColor(ShipStatus status, Color backColor)\r
-        {\r
-            switch (status.DamageLevel)\r
-            {\r
-                case ShipStatus.Damage.Sunk:\r
-                    return Color.CornflowerBlue;\r
-                case ShipStatus.Damage.Badly:\r
-                    return CUDColors.Red;\r
-                case ShipStatus.Damage.Half:\r
-                    return CUDColors.Orange;\r
-                case ShipStatus.Damage.Small:\r
-                    return Color.FromArgb(240, 240, 0);\r
-                default:\r
-                    return backColor;\r
+            public void SetHp(int now, int max)\r
+            {\r
+                Set(new ShipStatus {NowHp = now, MaxHp = max});\r
+            }\r
+\r
+            public Color DamageColor(ShipStatus status)\r
+            {\r
+                switch (status.DamageLevel)\r
+                {\r
+                    case ShipStatus.Damage.Sunk:\r
+                        return Color.CornflowerBlue;\r
+                    case ShipStatus.Damage.Badly:\r
+                        return CUDColors.Red;\r
+                    case ShipStatus.Damage.Half:\r
+                        return CUDColors.Orange;\r
+                    case ShipStatus.Damage.Small:\r
+                        return Color.FromArgb(240, 240, 0);\r
+                    default:\r
+                        return InitialBackColor;\r
+                }\r
+            }\r
+\r
+            public void SetColor(ShipStatus status)\r
+            {\r
+                BackColor = DamageColor(status);\r
             }\r
         }\r
 \r
-        public void SetCond(ShipStatus status)\r
+        public sealed class Cond : ShipLabel\r
         {\r
-            if (status == null)\r
+            public Cond(Point location, int height)\r
+            {\r
+                Location = location;\r
+                Size = new Size(24, height);\r
+                TextAlign = ContentAlignment.MiddleRight;\r
+            }\r
+\r
+            public override void Reset()\r
             {\r
                 Text = "";\r
-                BackColor = PresetColor;\r
-                return;\r
-            }\r
-            var cond = status.Cond;\r
-            Text = cond.ToString("D");\r
-            BackColor = cond >= 50\r
-                ? CUDColors.Yellow\r
-                : cond >= 30\r
-                    ? PresetColor\r
-                    : cond >= 20\r
-                        ? CUDColors.Orange\r
-                        : CUDColors.Red;\r
-        }\r
+                BackColor = InitialBackColor;\r
+            }\r
 \r
-        public void SetLevel(ShipStatus status)\r
-        {\r
-            Text = status?.Level.ToString("D");\r
+            public override void Set(ShipStatus status)\r
+            {\r
+                var cond = status.Cond;\r
+                Text = cond.ToString("D");\r
+                BackColor = cond >= 50\r
+                    ? CUDColors.Yellow\r
+                    : cond >= 30\r
+                        ? InitialBackColor\r
+                        : cond >= 20\r
+                            ? CUDColors.Orange\r
+                            : CUDColors.Red;\r
+            }\r
         }\r
 \r
-        public void SetExpToNext(ShipStatus status)\r
+        public sealed class Level : ShipLabel\r
         {\r
-            Text = status?.ExpToNext.ToString("D");\r
-        }\r
+            public Level(Point location, int height)\r
+            {\r
+                Location = location;\r
+                Size = new Size(24, height);\r
+                TextAlign = ContentAlignment.MiddleRight;\r
+            }\r
 \r
-        public void SetRepairTime(ShipStatus status)\r
-        {\r
-            if (status == null)\r
+            public override void Reset()\r
             {\r
                 Text = "";\r
-                return;\r
             }\r
-            SetRepairTime(status.RepairTime);\r
-        }\r
 \r
-        public void SetRepairTime(TimeSpan span)\r
-        {\r
-            Text = $@"{(int)span.TotalHours:d2}:{span:mm\:ss}";\r
+            public override void Set(ShipStatus status)\r
+            {\r
+                Text = status.Level.ToString("D");\r
+            }\r
         }\r
 \r
-        public void SetFleet(ShipStatus status)\r
+        public sealed class Exp : ShipLabel\r
         {\r
-            Text = status?.Fleet == null ? "" : new[] {"1", "2", "3", "4"}[status.Fleet.Number];\r
-        }\r
+            public Exp(Point location, int height)\r
+            {\r
+                Location = location;\r
+                Size = new Size(42, height);\r
+                TextAlign = ContentAlignment.MiddleRight;\r
+            }\r
 \r
-        protected override void OnSizeChanged(EventArgs args)\r
-        {\r
-            base.OnSizeChanged(args);\r
-            KeepAnchorRight();\r
-        }\r
+            public override void Reset()\r
+            {\r
+                Text = "";\r
+            }\r
 \r
-        protected override void OnLayout(LayoutEventArgs args)\r
-        {\r
-            base.OnLayout(args);\r
-            KeepAnchorRight();\r
+            public override void Set(ShipStatus status)\r
+            {\r
+                Text = status.ExpToNext.ToString("D");\r
+            }\r
         }\r
 \r
-        private void KeepAnchorRight()\r
+        public sealed class Fleet : ShipLabel\r
         {\r
-            if (!AnchorRight)\r
-                return;\r
-            if (_right == int.MinValue || _left != Left)\r
-            {\r
-                _right = Right;\r
-                _left = Left;\r
-                return;\r
-            }\r
-            if (_right == Right)\r
-                return;\r
-            _left -= Right - _right;\r
-            Location = new Point(_left, Top);\r
+            public Fleet(Point location)\r
+            {\r
+                Location = location;\r
+                AutoSize = true;\r
+            }\r
+\r
+            public override void Reset()\r
+            {\r
+                Text = "";\r
+            }\r
+\r
+            public override void Set(ShipStatus status)\r
+            {\r
+                Text = status.Fleet == null ? "" : new[] {"1", "2", "3", "4"}[status.Fleet.Number];\r
+            }\r
         }\r
 \r
-        protected override void OnPaint(PaintEventArgs e)\r
+        public sealed class RepairTime : ShipLabel\r
         {\r
-            base.OnPaint(e);\r
-            if ((_slotStatus & SlotStatus.NormalEmpty) != 0)\r
+            public RepairTime(Point location)\r
             {\r
-                e.Graphics.DrawRectangle(\r
-                    Pens.Black,\r
-                    ClientSize.Width - 3 * ScaleFactor.Width, 0,\r
-                    2 * ScaleFactor.Width, 5 * ScaleFactor.Height);\r
+                Location = location;\r
+                AutoSize = true;\r
             }\r
-            else if ((_slotStatus & SlotStatus.SemiEquipped) != 0)\r
+\r
+            public override void Reset()\r
             {\r
-                e.Graphics.DrawLine(\r
-                    Pens.Black,\r
-                    ClientSize.Width - 1 * ScaleFactor.Width, 0,\r
-                    ClientSize.Width - 1 * ScaleFactor.Width, 5 * ScaleFactor.Height);\r
+                Text = "";\r
             }\r
-            if ((_slotStatus & SlotStatus.ExtraEmpty) != 0)\r
+\r
+            public override void Set(ShipStatus status)\r
             {\r
-                e.Graphics.DrawRectangle(\r
-                    Pens.Black,\r
-                    ClientSize.Width - 3 * ScaleFactor.Width, 8 * ScaleFactor.Height,\r
-                    2 * ScaleFactor.Width, 3 * ScaleFactor.Height);\r
+                SetRepairTime(status.RepairTime);\r
             }\r
-        }\r
 \r
-        public void Scale()\r
-        {\r
-            Scale(ScaleFactor);\r
+            public void SetRepairTime(TimeSpan span)\r
+            {\r
+                Text = $@"{(int)span.TotalHours:d2}:{span:mm\:ss}";\r
+            }\r
         }\r
     }\r
 }
\ No newline at end of file