private void ResizeRadioButtons(int n) { if (radioButtons != null && radioButtons.Length == n) { return; } if (radioButtons != null) { foreach (var i in All.Int(radioButtons.Length)) { var r = radioButtons[i]; groupBox1.Controls.Remove(r); r.Dispose(); var p = pictureBoxes[i]; // PictureBoxは必ず持っているとは限らない。 if (p != null) { groupBox1.Controls.Remove(p); p.Dispose(); } var img = images[i]; if (img != null) { img.Dispose(); } } } radioButtons = new Control[n]; pictureBoxes = new Control[n]; images = new ImageLoader[n]; foreach (var i in All.Int(n)) { var texts = SelectionTexts[i].Split(','); if (texts.Length < 2) { continue; } var r = new RadioButton(); var x = (pictureBox1.Width + groupBox1.Margin.Left * 2) * i + Margin.Left * 5; var rx = x /* + radioButton1.Location.X */; r.Location = new Point(rx, radioButton1.Location.Y); r.Text = texts[0]; var j = i; // copy for lambda's capture r.CheckedChanged += (sender, args) => { if (r.Checked) { // 再起動するように警告表示 if (ViewModel.WarningRestart) { TheApp.app.MessageShow("この変更が反映するのは次回起動時です。", MessageShowType.Confirmation); } ViewModel.Selection = j; } }; r.Checked = i == ViewModel.Selection; radioButtons[i] = r; groupBox1.Controls.Add(r); var p = new PictureBox(); var x2 = x; p.Location = new Point(x2, pictureBox1.Location.Y); p.Size = pictureBox1.Size; // サイズは固定しておいたほうが扱いやすい p.Click += (sender, args) => { r.Checked = true; /* RadioButtonがクリックされたのと同等の扱いをしてやる*/ }; p.BorderStyle = BorderStyle.FixedSingle; pictureBoxes[i] = p; groupBox1.Controls.Add(p); var img = new ImageLoader(); var tmp_img = new ImageLoader(); var path = Path.Combine(ImageFolder, texts[1]); tmp_img.Load(path); images[i] = tmp_img.CreateAndCopy(p.Width, p.Height); p.Image = images[i].image; // ToolTipの追加。 if (texts.Length >= 3) { var tips = texts[2]; toolTip1.SetToolTip(r, tips); toolTip1.SetToolTip(p, tips); } } Invalidate(); }
/// <summary> /// [UI Thread] : EngineDefineが変更になった時のハンドラ。これが画面に反映される。 /// </summary> /// <param name="args"></param> private void EngineDefineChanged(PropertyChangedEventArgs args) { var engineDefine = args.value as EngineDefine; if (engineDefine == null) { // 全部クリア var label_list = new[] { label1, label2, label3, label4, label5 }; foreach (var label in label_list) { label.Text = null; } textBox1.Text = null; banner_mini.Dispose(); pictureBox1.Image = null; button1.Enabled = false; return; } // 「選択」ボタン有効化 button1.Enabled = true; // ソフト名 label1.Text = engineDefine.DescriptionSimple; // ソフトの説明文 textBox1.Text = engineDefine.Description; // バナー // ImageLoader経由での読み込みなのでなければ「×」画像になるだけ。 int w = pictureBox1.Width; int h = pictureBox1.Height; if (!File.Exists(engineDefine.BannerFileName)) { // ファイルがないのでNO BANNERのbannerにする。 var banner = TheApp.app.ImageManager.NoBannerImage; banner_mini.Dispose(); banner_mini = banner.CreateAndCopy(w, h); } else { var banner = new ImageLoader(); banner.Load(engineDefine.BannerFileName); banner_mini.Dispose(); banner_mini = banner.CreateAndCopy(w, h); } pictureBox1.Image = banner_mini.image; // ソフトの使用メモリ var free_memory = ViewModel.FreePhysicalMemory; var engine_define = ViewModel.EngineDefine; var work_memory = engine_define.EvalMemory + engine_define.WorkingMemory + engine_define.StackPerThread * 4; var required_memory = work_memory + engine_define.MinimumHashMemory; var is_enough = required_memory <= free_memory; label2.Text = "必要メモリ : "; label3.Text = $"本体 {work_memory}[MB] + HASH {engineDefine.MinimumHashMemory}[MB]以上 = "; label4.Text = required_memory.ToString(); label4.ForeColor = is_enough ? Color.Black : Color.Red; label5.Text = "[MB]以上" + (is_enough ? "≦" : ">") + $" 現在の空き物理メモリ {free_memory}[MB]"; // labelの位置を調整する。 var labels = new [] { label2, label3, label4, label5 }; for (int i = 1; i < labels.Length; ++i) { labels[i].Location = new Point(labels[i - 1].Location.X + labels[i - 1].Width, labels[i - 1].Location.Y); } }
// -- ViewModelの初期化 private void InitViewModel() { var vm = ViewModel; vm.AddPropertyChangedHandler("Color", (args) => { var color = (Color)args.value; groupBox1.Text = color == Color.BLACK ? "先手/下手" : "後手/上手"; }); #if false vm.AddPropertyChangedHandler("EngineSelectButton", (args) => { var b = (bool)args.value; button1.Enabled = b; // エンジン選択ボタン }); vm.AddPropertyChangedHandler("EngineOptionButton", (args) => { var b = (bool)args.value; button2.Enabled = b; // 詳細設定ボタン }); #endif vm.AddPropertyChangedHandler("EngineDefineFolderPath", (args) => { SuspendLayout(); try { var player = TheApp.app.Config.GameSetting.PlayerSetting(vm.Color); var folderPath = (string)args.value; var engine_define_ex = TheApp.app.EngineDefines.Find(x => x.FolderPath == folderPath); button2.Enabled = engine_define_ex != null; if (engine_define_ex == null) { // バナー等をクリアしてから返る。 pictureBox1.Image = null; comboBox1.Items.Clear(); textBox2.Text = null; toolTip1.SetToolTip(pictureBox1, null); return; } var engine_define = engine_define_ex.EngineDefine; // -- バナーの設定 // (w,h)=(320,100)のつもりだが、dpi scalingのせいで // 環境によって異なるのでここで再取得してそれに合わせる。 int w = pictureBox1.Width; int h = pictureBox1.Height; // バナーファイルの設定 // ファイルがないならNO BANNERの画像。 var banner_file_name = engine_define.BannerFileName; // 一つ前のがあるなら解放 banner_mini.Dispose(); if (!System.IO.File.Exists(banner_file_name)) { var banner = TheApp.app.ImageManager.NoBannerImage; banner_mini = banner.CreateAndCopy(w, h); // これはImageManagerからもらったやつなので解放してはならない。 } else { using (var banner = new ImageLoader()) { banner.Load(engine_define.BannerFileName); banner_mini = banner.CreateAndCopy(w, h); } } pictureBox1.Image = banner_mini.image; // -- 対局者名の設定 //// 人間が選択されている時は、Radioボタンのハンドラでプレイヤー名が「先手」「後手」になるのでここでは設定しない。 //if (player.IsCpu) // textBox1.Text = engine_define.DisplayName; // → ここでやるべきではない。エンジンを選択した瞬間のみに代入するようにしたほうが良い。 // -- プリセットのコンボボックス var PlayerSetting = TheApp.app.Config.GameSetting.PlayerSetting(vm.Color); // プリセットをコンボボックスに反映 // SuspendLayout()~ResumeLayout()中にやらないとイベントハンドラが呼び出されておかしいことになる。 // このプリセットのどこを選ぶかは、この外側でこのcomboBoxとdata bindしている変数を書き換えることで示す。 comboBox1.Items.Clear(); foreach (var e in engine_define.Presets) { comboBox1.Items.Add(e.Name); } // 前回と同じものを選んでおいたほうが使いやすいかも。 // → 「やねうら王」初段 のあと「tanuki」を選んだ時も初段であって欲しい気がするが…。 // 先後入替えなどもあるので、やはりそれは良くない気がする。 // エンジンを選択したのだから、対局相手はコンピュータになっていて欲しいはず。 // → エンジンを選択したとは限らない。先後入替えでもここが呼び出される。 //radioButton2.Checked = true; // ToolTipを更新しておく。Descriptionは改行なしに書かれているはずなので適宜改行する。 toolTip1.SetToolTip(pictureBox1, engine_define.Description.UnicodeInsertEvery("\r\n", 60)); } finally { ResumeLayout(); } }); vm.AddPropertyChangedHandler("EngineSelected", args => { // -- CPUの選択 // この変更がトリガーになってエンジン選択ダイアログが出るイベントが発生すると嫌だ.. radioButton2.Checked = true; }); }
private void InitViewModel() { // 検討開始ボタンは、検討エンジンが選択されていないと有効にならない。 button3.Enabled = false; ViewModel.AddPropertyChangedHandler("EngineDefineFolderPath", (args) => { SuspendLayout(); try { var folderPath = (string)args.value; var engine_define_ex = TheApp.app.EngineDefines.Find(x => x.FolderPath == folderPath); Setting.EngineDefineFolderPath = folderPath; button2.Enabled = engine_define_ex != null; button3.Enabled = engine_define_ex != null; if (engine_define_ex == null) { // バナー等をクリアしてから返る。 pictureBox1.Image = null; textBox1.Text = null; return; } var engine_define = engine_define_ex.EngineDefine; // エンジン名の設定 textBox1.Text = engine_define.DisplayName; // -- バナーの設定 // (w,h)=(320,100)のつもりだが、dpi scalingのせいで // 環境によって異なるのでここで再取得してそれに合わせる。 int w = pictureBox1.Width; int h = pictureBox1.Height; // バナーファイルの設定 // ファイルがないならNO BANNERの画像。 var banner_file_name = engine_define.BannerFileName; // 一つ前のがあるなら解放 banner_mini.Dispose(); if (!System.IO.File.Exists(banner_file_name)) { var banner = TheApp.app.ImageManager.NoBannerImage; banner_mini = banner.CreateAndCopy(w, h); // これはImageManagerからもらったやつなので解放してはならない。 } else { using (var banner = new ImageLoader()) { banner.Load(engine_define.BannerFileName); banner_mini = banner.CreateAndCopy(w, h); } } pictureBox1.Image = banner_mini.image; } finally { ResumeLayout(); } }); ViewModel.AddPropertyChangedHandler("EngineSelectionButtonClicked", _ => CreateEngineSelectionDialog()); ViewModel.AddPropertyChangedHandler("DialogType", (args) => { var dialogType = ViewModel.DialogType; switch (dialogType) { case ConsiderationEngineSettingDialogType.ConsiderationSetting: Text = "検討エンジン設定"; label3.Text = "検討で使う思考エンジン:"; groupBox1.Enabled = true; break; case ConsiderationEngineSettingDialogType.MateSetting: Text = "詰将棋エンジン設定"; label3.Text = "詰検討で使う思考エンジン:"; groupBox1.Enabled = true; // 詰将棋エンジン側、対応したので有効にしておく。 break; } }); }
private void InitViewModel() { // 検討開始ボタンは、検討エンジンが選択されていないと有効にならない。 button3.Enabled = false; ViewModel.AddPropertyChangedHandler("EngineDefineFolderPath", (args) => { using (var slb = new SuspendLayoutBlock(this)) { var folderPath = (string)args.value; var engine_define_ex = TheApp.app.EngineDefines.Find(x => x.FolderPath == folderPath); Setting.EngineDefineFolderPath = folderPath; button2.Enabled = engine_define_ex != null; button3.Enabled = engine_define_ex != null; if (engine_define_ex == null) { // バナー等をクリアしてから返る。 pictureBox1.Image = null; textBox1.Text = null; return; } var engine_define = engine_define_ex.EngineDefine; // エンジン名の設定 textBox1.Text = engine_define.DisplayName; // -- バナーの設定 // (w,h)=(320,100)のつもりだが、dpi scalingのせいで // 環境によって異なるのでここで再取得してそれに合わせる。 int w = pictureBox1.Width; int h = pictureBox1.Height; // バナーファイルの設定 // ファイルがないならNO BANNERの画像。 var banner_file_name = engine_define.BannerFileName; // 一つ前のがあるなら解放 banner_mini.Dispose(); if (!System.IO.File.Exists(banner_file_name)) { var banner = TheApp.app.ImageManager.NoBannerImage; banner_mini = banner.CreateAndCopy(w, h); // これはImageManagerからもらったやつなので解放してはならない。 } else { using (var banner = new ImageLoader()) { banner.Load(engine_define.BannerFileName); banner_mini = banner.CreateAndCopy(w, h); } } pictureBox1.Image = banner_mini.image; // 詰将棋エンジンなら、nodes , depthを指定できるかは、USI2.0拡張で拡張されていない限りむりぽ。 if (ViewModel.DialogType == ConsiderationEngineSettingDialogType.MateSetting) { // USIプロトコルではgo mateのあと時間[ms]を指定するようになっている。 // 現状、プロトコル的に対応不可能であるな。 // go mate "nodes" // go mate "depth" // のような拡張を受け付けるようにする。 var nodes_enable = engine_define.SupportedExtendedProtocol.Contains(ExtendedProtocol.GoMateNodesExtension); var depth_enable = engine_define.SupportedExtendedProtocol.Contains(ExtendedProtocol.GoMateDepthExtension); radioButton3.Enabled = nodes_enable; radioButton4.Enabled = depth_enable; // 上で無効化したRadioButtonが選択されている可能性がある。 // その場合、radioButton1を選択しなおす。 if ((!radioButton3.Enabled && radioButton3.Checked) || (!radioButton4.Enabled && radioButton4.Checked)) { radioButton1.Checked = true; } } } }); ViewModel.AddPropertyChangedHandler("EngineSelectionButtonClicked", _ => CreateEngineSelectionDialog()); ViewModel.AddPropertyChangedHandler("DialogType", (args) => { var dialogType = ViewModel.DialogType; switch (dialogType) { case ConsiderationEngineSettingDialogType.ConsiderationSetting: Text = "検討エンジン設定"; label3.Text = "検討で使う思考エンジン:"; groupBox1.Enabled = true; break; case ConsiderationEngineSettingDialogType.MateSetting: Text = "詰将棋エンジン設定"; label3.Text = "詰検討で使う思考エンジン:"; groupBox1.Enabled = true; // 詰将棋エンジン側、対応したので有効にしておく。 break; } }); }