diff --git a/src/KFlearning.Core/API/FlutterGitClient.cs b/src/KFlearning.Core/API/FlutterGitClient.cs new file mode 100644 index 0000000..f2cdcad --- /dev/null +++ b/src/KFlearning.Core/API/FlutterGitClient.cs @@ -0,0 +1,72 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Reflection; +using System.Threading.Tasks; + +namespace KFlearning.Core.API +{ + public interface IFlutterGitClient + { + string GetFlutterDownloadUri(string version); + Task GetLatestFlutterVersion(); + } + + public class FlutterGitClient : IFlutterGitClient + { + private static HttpClient Client; + + static FlutterGitClient() + { + var version = Assembly.GetCallingAssembly().GetName().Version; + + Client = new HttpClient(); + Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json")); + Client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("KFlearning", $"{version.Major}.{version.Minor}.{version.Build}")); + } + + public async Task GetLatestFlutterVersion() + { + var response = await Client.GetAsync("https://api.github.com/repos/flutter/flutter/git/refs/tags"); + var serializer = new JsonSerializer(); + using (var bodyReader = new StreamReader(await response.Content.ReadAsStreamAsync())) + using (var jsonReader = new JsonTextReader(bodyReader)) + { + var tags = serializer.Deserialize>(jsonReader); + var latest = tags.Where(x => !x.Ref.Contains("pre")).LastOrDefault(); + if (latest == null) + { + throw new KFlearningException("Tidak dapat menemukan versi Flutter! Silakan download manual."); + } + + return GetVersionFromTag(latest.Ref); + } + } + + public string GetFlutterDownloadUri(string version) + { + return $"https://storage.googleapis.com/flutter_infra/releases/stable/windows/flutter_windows_{version}-stable.zip"; + } + + private string GetVersionFromTag(string tag) + { + return tag.Split('/').Last(); + } + + public partial class FlutterGitTag + { + [JsonProperty("ref")] + public string Ref { get; set; } + + [JsonProperty("node_id")] + public string NodeId { get; set; } + + [JsonProperty("url")] + public Uri Url { get; set; } + } + } +} diff --git a/src/KFlearning/KFlearning.csproj b/src/KFlearning/KFlearning.csproj index b20d1e3..0fd9881 100644 --- a/src/KFlearning/KFlearning.csproj +++ b/src/KFlearning/KFlearning.csproj @@ -65,10 +65,13 @@ + + + @@ -101,6 +104,12 @@ CreateProjectForm.cs + + Form + + + FlutterInstallView.cs + Form @@ -130,6 +139,9 @@ CreateProjectForm.cs + + FlutterInstallView.cs + StartupForm.cs @@ -150,10 +162,13 @@ - 4.4.0 + 4.4.1 - 5.0.1 + 5.1.1 + + + 1.15.0 diff --git a/src/KFlearning/Services/FlutterInstallService.cs b/src/KFlearning/Services/FlutterInstallService.cs new file mode 100644 index 0000000..5fcb266 --- /dev/null +++ b/src/KFlearning/Services/FlutterInstallService.cs @@ -0,0 +1,247 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Ionic.Zip; +using KFlearning.Core.API; +using KFlearning.Core.Extensions; +using KFlearning.Core.Services; + +namespace KFlearning.Services +{ + public interface IFlutterInstallService + { + string FlutterVersion { get; } + string InstallPath { get; set; } + + event EventHandler InstallReady; + event EventHandler ProgressChanged; + event EventHandler InstallFinished; + + void Cancel(); + void Install(); + void PreparationStep(); + } + + public class FlutterInstallService : IFlutterInstallService + { + private readonly WebClient _webClient; + private readonly IFlutterGitClient _flutterService; + + private CancellationTokenSource _cancellationSource; + private string _downloadPath = ""; + + public event EventHandler InstallReady; + public event EventHandler ProgressChanged; + public event EventHandler InstallFinished; + + public string FlutterVersion { get; private set; } + public string InstallPath { get; set; } + + public FlutterInstallService(IFlutterGitClient flutterService, WebClient webClient, IPathManager pathManager) + { + InstallPath = pathManager.GetPath(PathKind.FlutterInstallDirectory); + + _flutterService = flutterService; + _webClient = webClient; + _webClient.DownloadProgressChanged += WebClient_DownloadProgressChanged; + } + + #region Public Methods + + public void Install() + { + _cancellationSource = new CancellationTokenSource(); + ExecuteSteps(); + } + + public void Cancel() + { + _webClient.CancelAsync(); + _cancellationSource.Cancel(); + } + + public void PreparationStep() + { + Task.Run(async () => + { + try + { + FlutterVersion = await _flutterService.GetLatestFlutterVersion(); + OnInstallReady(this, new FlutterInstallReadyEventArgs + { + Ready = true + }); + } + catch (Exception ex) + { + OnInstallReady(this, new FlutterInstallReadyEventArgs + { + Ready = false, + ErrorMessage = ex.Message + }); + } + }); + } + + #endregion + + #region Install Steps + + private void ExecuteSteps() + { + Task.Run(async () => + { + try + { + // step 1 --- download Flutter SDK + OnProgressChanged(this, new FlutterInstallProgressEventArgs + { + ProgressPercentage = 0, + Status = "Memulai unduhan..." + }); + + _downloadPath = Path.GetTempFileName(); + var uri = _flutterService.GetFlutterDownloadUri(FlutterVersion); + + await _webClient.DownloadFileTaskAsync(new Uri(uri), _downloadPath); + + // step 2 --- extract to installfolder + OnProgressChanged(this, new FlutterInstallProgressEventArgs + { + ProgressPercentage = 0, + Status = "Memulai ekstraksi..." + }); + + if (IsProcessCancelled()) return; + using (var zip = new ZipFile(_downloadPath)) + { + var totalEntries = zip.Entries.Count; + var processedEntry = 0; + foreach (var entry in zip) // non-parallelism, the lib doesn't support parallel + { + if (IsProcessCancelled()) return; + + var path = Path.GetFullPath(Path.Combine(InstallPath, "../" + entry.FileName)); + if (entry.IsDirectory) + { + Directory.CreateDirectory(path); + } + else + { + using (var fs = new FileStream(path, FileMode.Create)) + { + entry.Extract(fs); + } + } + + Interlocked.Increment(ref processedEntry); + OnProgressChanged(this, new FlutterInstallProgressEventArgs + { + ProgressPercentage = (int)((double)processedEntry / totalEntries * 100), + Status = "Memindahkan direktori..." + }); + } + } + + // step 3 --- set environment variable + OnProgressChanged(this, new FlutterInstallProgressEventArgs + { + ProgressPercentage = 70, + Status = "Mengatur environment variable..." + }); + + if (IsProcessCancelled()) return; + var pathEnv = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.User); + if (pathEnv?.Contains(@"flutter\bin") == false) + { + pathEnv += ";" + Path.Combine(InstallPath, @"bin"); + Environment.SetEnvironmentVariable("PATH", pathEnv, EnvironmentVariableTarget.User); + } + + // --- all done! + OnInstallFinished(this, new FlutterInstallFinishedEventArgs + { + ErrorMessage = "Instalasi selesai.", + Success = true + }); + } + catch (Exception e) + { + OnInstallFinished(this, new FlutterInstallFinishedEventArgs + { + ErrorMessage = e.Message, + Success = false + }); + } + }); + } + + private bool IsProcessCancelled() + { + if (!_cancellationSource.IsCancellationRequested) return false; + OnInstallFinished(this, new FlutterInstallFinishedEventArgs + { + ErrorMessage = "Instalasi dibatalkan.", + Success = false + }); + + return true; + } + + #endregion + + #region Event Handlers + + private void WebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) + { + OnProgressChanged(this, new FlutterInstallProgressEventArgs + { + ProgressPercentage = e.ProgressPercentage, + Status = "Mengunduh..." + }); + } + + #endregion + + #region Event Invocators + + private void OnInstallReady(object sender, FlutterInstallReadyEventArgs e) + { + InstallReady?.Invoke(sender, e); + } + + private void OnProgressChanged(object sender, FlutterInstallProgressEventArgs e) + { + ProgressChanged?.Invoke(sender, e); + } + + private void OnInstallFinished(object sender, FlutterInstallFinishedEventArgs e) + { + InstallFinished?.Invoke(sender, e); + } + + #endregion + } + + public class FlutterInstallReadyEventArgs : EventArgs + { + public bool Ready { get; set; } + public string ErrorMessage { get; set; } + } + + public class FlutterInstallProgressEventArgs : EventArgs + { + public int ProgressPercentage { get; set; } + public string Status { get; set; } + } + + public class FlutterInstallFinishedEventArgs : EventArgs + { + public bool Success { get; set; } + public string ErrorMessage { get; set; } + } +} diff --git a/src/KFlearning/Services/KFlearningModulesInstaller.cs b/src/KFlearning/Services/KFlearningModulesInstaller.cs index aed8041..6b04fb1 100644 --- a/src/KFlearning/Services/KFlearningModulesInstaller.cs +++ b/src/KFlearning/Services/KFlearningModulesInstaller.cs @@ -3,6 +3,7 @@ using Castle.Windsor; using KFlearning.TemplateProvider; using KFlearning.Views; +using System.Net; namespace KFlearning.Services { @@ -11,6 +12,8 @@ public class KFlearningModulesInstaller : IWindsorInstaller public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register( + Component.For().ImplementedBy().LifestyleTransient(), + // views Classes.FromThisAssembly() .InSameNamespaceAs() diff --git a/src/KFlearning/Services/ProjectService.cs b/src/KFlearning/Services/ProjectService.cs index a9c43c4..1bef40e 100644 --- a/src/KFlearning/Services/ProjectService.cs +++ b/src/KFlearning/Services/ProjectService.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using KFlearning.Core.Extensions; using KFlearning.Core.Services; using KFlearning.Models; using Newtonsoft.Json; @@ -74,7 +75,7 @@ public bool IsExists(string path) public string GetPathForProject(string title, string basePath = null) { return Path.Combine(basePath ?? _path.GetPath(PathKind.DefaultProjectRoot), - _path.StripInvalidPathName(title)); + PathHelpers.StripInvalidPathName(title)); } private void Save(Project project) diff --git a/src/KFlearning/Views/FlutterInstallView.Designer.cs b/src/KFlearning/Views/FlutterInstallView.Designer.cs new file mode 100644 index 0000000..d959123 --- /dev/null +++ b/src/KFlearning/Views/FlutterInstallView.Designer.cs @@ -0,0 +1,225 @@ + +namespace KFlearning.Views +{ + partial class FlutterInstallView + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.panel1 = new System.Windows.Forms.Panel(); + this.label3 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.txtInstallPath = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.lblFlutterVersion = new System.Windows.Forms.Label(); + this.lblStatus = new System.Windows.Forms.Label(); + this.prgProgress = new System.Windows.Forms.ProgressBar(); + this.cmdBrowse = new System.Windows.Forms.LinkLabel(); + this.lblPercent = new System.Windows.Forms.Label(); + this.cmdInstall = new System.Windows.Forms.Button(); + this.fbd = new System.Windows.Forms.FolderBrowserDialog(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // panel1 + // + this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(47)))), ((int)(((byte)(49))))); + this.panel1.Controls.Add(this.label3); + this.panel1.Controls.Add(this.label5); + this.panel1.Dock = System.Windows.Forms.DockStyle.Top; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(387, 48); + this.panel1.TabIndex = 15; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Wingdings", 18F); + this.label3.Location = new System.Drawing.Point(17, 12); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(33, 26); + this.label3.TabIndex = 2; + this.label3.Text = "Þ"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Font = new System.Drawing.Font("Segoe UI", 15F); + this.label5.Location = new System.Drawing.Point(59, 9); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(167, 28); + this.label5.TabIndex = 1; + this.label5.Text = "Install Flutter SDK"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(38, 97); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(77, 15); + this.label1.TabIndex = 16; + this.label1.Text = "Lokasi install:"; + // + // txtInstallPath + // + this.txtInstallPath.Location = new System.Drawing.Point(132, 94); + this.txtInstallPath.Name = "txtInstallPath"; + this.txtInstallPath.ReadOnly = true; + this.txtInstallPath.Size = new System.Drawing.Size(168, 23); + this.txtInstallPath.TabIndex = 17; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(38, 72); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(71, 15); + this.label2.TabIndex = 18; + this.label2.Text = "Versi Flutter:"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(38, 142); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(89, 15); + this.label4.TabIndex = 19; + this.label4.Text = "Proses instalasi:"; + // + // lblFlutterVersion + // + this.lblFlutterVersion.AutoSize = true; + this.lblFlutterVersion.Location = new System.Drawing.Point(129, 72); + this.lblFlutterVersion.Name = "lblFlutterVersion"; + this.lblFlutterVersion.Size = new System.Drawing.Size(16, 15); + this.lblFlutterVersion.TabIndex = 20; + this.lblFlutterVersion.Text = "..."; + // + // lblStatus + // + this.lblStatus.AutoSize = true; + this.lblStatus.Location = new System.Drawing.Point(38, 187); + this.lblStatus.Name = "lblStatus"; + this.lblStatus.Size = new System.Drawing.Size(139, 15); + this.lblStatus.TabIndex = 21; + this.lblStatus.Text = "Menunggu versi Flutter..."; + // + // prgProgress + // + this.prgProgress.Location = new System.Drawing.Point(41, 160); + this.prgProgress.Name = "prgProgress"; + this.prgProgress.Size = new System.Drawing.Size(304, 18); + this.prgProgress.TabIndex = 22; + // + // cmdBrowse + // + this.cmdBrowse.AutoSize = true; + this.cmdBrowse.Enabled = false; + this.cmdBrowse.LinkColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(160)))), ((int)(((byte)(230))))); + this.cmdBrowse.Location = new System.Drawing.Point(306, 97); + this.cmdBrowse.Name = "cmdBrowse"; + this.cmdBrowse.Size = new System.Drawing.Size(39, 15); + this.cmdBrowse.TabIndex = 23; + this.cmdBrowse.TabStop = true; + this.cmdBrowse.Text = "Pilih..."; + this.cmdBrowse.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.cmdBrowse_LinkClicked); + // + // lblPercent + // + this.lblPercent.AutoSize = true; + this.lblPercent.Location = new System.Drawing.Point(310, 187); + this.lblPercent.Name = "lblPercent"; + this.lblPercent.Size = new System.Drawing.Size(23, 15); + this.lblPercent.TabIndex = 24; + this.lblPercent.Text = "0%"; + // + // cmdInstall + // + this.cmdInstall.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(80)))), ((int)(((byte)(80))))); + this.cmdInstall.Enabled = false; + this.cmdInstall.FlatAppearance.BorderSize = 0; + this.cmdInstall.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.cmdInstall.Location = new System.Drawing.Point(234, 227); + this.cmdInstall.Name = "cmdInstall"; + this.cmdInstall.Size = new System.Drawing.Size(111, 32); + this.cmdInstall.TabIndex = 25; + this.cmdInstall.Text = "Install Flutter"; + this.cmdInstall.UseVisualStyleBackColor = false; + this.cmdInstall.Click += new System.EventHandler(this.cmdInstall_Click); + // + // FlutterInstallView + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(70)))), ((int)(((byte)(73))))); + this.ClientSize = new System.Drawing.Size(387, 285); + this.Controls.Add(this.cmdInstall); + this.Controls.Add(this.lblPercent); + this.Controls.Add(this.cmdBrowse); + this.Controls.Add(this.prgProgress); + this.Controls.Add(this.lblStatus); + this.Controls.Add(this.lblFlutterVersion); + this.Controls.Add(this.label4); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtInstallPath); + this.Controls.Add(this.label1); + this.Controls.Add(this.panel1); + this.Font = new System.Drawing.Font("Segoe UI", 9F); + this.ForeColor = System.Drawing.Color.White; + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "FlutterInstallView"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Install Flutter SDK"; + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox txtInstallPath; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label lblFlutterVersion; + private System.Windows.Forms.Label lblStatus; + private System.Windows.Forms.ProgressBar prgProgress; + private System.Windows.Forms.LinkLabel cmdBrowse; + private System.Windows.Forms.Label lblPercent; + private System.Windows.Forms.Button cmdInstall; + private System.Windows.Forms.FolderBrowserDialog fbd; + } +} \ No newline at end of file diff --git a/src/KFlearning/Views/FlutterInstallView.cs b/src/KFlearning/Views/FlutterInstallView.cs new file mode 100644 index 0000000..7cefa99 --- /dev/null +++ b/src/KFlearning/Views/FlutterInstallView.cs @@ -0,0 +1,100 @@ +using KFlearning.Services; +using System; +using System.Windows.Forms; + +namespace KFlearning.Views +{ + public partial class FlutterInstallView : Form + { + private readonly IFlutterInstallService _installService = Program.Container.Resolve(); + + public FlutterInstallView() + { + InitializeComponent(); + _installService.InstallReady += InstallService_InstallReady; + _installService.ProgressChanged += InstallService_ProgressChanged; + _installService.InstallFinished += InstallService_InstallFinished; + } + + #region FlutterInstallService Event Handlers + + private void InstallService_InstallReady(object sender, FlutterInstallReadyEventArgs e) + { + if (InvokeRequired) + { + Invoke(new Action(InstallService_InstallReady), sender, e); + } + else + { + cmdInstall.Enabled = e.Ready; + cmdBrowse.Enabled = e.Ready; + + lblStatus.Text = e.Ready ? "Siap." : e.ErrorMessage; + lblFlutterVersion.Text = e.Ready ? _installService.FlutterVersion : "Tidak diketahui."; + txtInstallPath.Text = _installService.InstallPath; + } + } + + private void InstallService_ProgressChanged(object sender, FlutterInstallProgressEventArgs e) + { + if (InvokeRequired) + { + Invoke(new Action(InstallService_ProgressChanged), sender, e); + } + else + { + prgProgress.Value = e.ProgressPercentage; + lblStatus.Text = e.Status; + lblPercent.Text = $"{e.ProgressPercentage}%"; + } + } + + private void InstallService_InstallFinished(object sender, FlutterInstallFinishedEventArgs e) + { + if (InvokeRequired) + { + Invoke(new Action(InstallService_InstallFinished), sender, e); + } + else + { + cmdBrowse.Enabled = true; + cmdInstall.Enabled = true; + + lblStatus.Text = e.ErrorMessage; + prgProgress.Value = 0; + lblPercent.Text = "0%"; + } + } + + #endregion + + protected override void OnLoad(EventArgs e) + { + _installService.PreparationStep(); + } + + private void cmdBrowse_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + if (fbd.ShowDialog() != DialogResult.OK) return; + txtInstallPath.Text = fbd.SelectedPath; + } + + private void cmdInstall_Click(object sender, EventArgs e) + { + if (cmdInstall.Text == "Install Flutter") + { + cmdBrowse.Enabled = false; + cmdInstall.Text = "Batal"; + + _installService.InstallPath = txtInstallPath.Text; + _installService.Install(); + } + else + { + cmdInstall.Enabled = false; + cmdInstall.Text = "Install Flutter"; + _installService.Cancel(); + } + } + } +} diff --git a/src/KFlearning/Views/FlutterInstallView.resx b/src/KFlearning/Views/FlutterInstallView.resx new file mode 100644 index 0000000..ccf69ad --- /dev/null +++ b/src/KFlearning/Views/FlutterInstallView.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/src/KFlearning/Views/StartupForm.Designer.cs b/src/KFlearning/Views/StartupForm.Designer.cs index ff0d9d5..8740fca 100644 --- a/src/KFlearning/Views/StartupForm.Designer.cs +++ b/src/KFlearning/Views/StartupForm.Designer.cs @@ -48,11 +48,15 @@ private void InitializeComponent() this.label5 = new System.Windows.Forms.Label(); this.panel5 = new System.Windows.Forms.Panel(); this.label7 = new System.Windows.Forms.Label(); + this.panel4 = new System.Windows.Forms.Panel(); + this.label2 = new System.Windows.Forms.Label(); + this.cmdFlutterInstall = new System.Windows.Forms.Button(); this.panel1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.panel2.SuspendLayout(); this.panel3.SuspendLayout(); this.panel5.SuspendLayout(); + this.panel4.SuspendLayout(); this.SuspendLayout(); // // cmdNewProject @@ -89,7 +93,7 @@ private void InitializeComponent() this.cmdOpenProject.FlatAppearance.BorderSize = 0; this.cmdOpenProject.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.cmdOpenProject.Font = new System.Drawing.Font("Segoe UI", 11F); - this.cmdOpenProject.Location = new System.Drawing.Point(417, 133); + this.cmdOpenProject.Location = new System.Drawing.Point(417, 123); this.cmdOpenProject.Name = "cmdOpenProject"; this.cmdOpenProject.Size = new System.Drawing.Size(159, 37); this.cmdOpenProject.TabIndex = 3; @@ -227,7 +231,7 @@ private void InitializeComponent() this.label4.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(80)))), ((int)(((byte)(80))))); this.label4.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.label4.Font = new System.Drawing.Font("Wingdings", 20.25F); - this.label4.Location = new System.Drawing.Point(9, 3); + this.label4.Location = new System.Drawing.Point(9, 4); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(37, 30); this.label4.TabIndex = 16; @@ -238,7 +242,7 @@ private void InitializeComponent() this.panel3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.panel3.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(80)))), ((int)(((byte)(80))))); this.panel3.Controls.Add(this.label5); - this.panel3.Location = new System.Drawing.Point(417, 134); + this.panel3.Location = new System.Drawing.Point(417, 124); this.panel3.Name = "panel3"; this.panel3.Size = new System.Drawing.Size(54, 35); this.panel3.TabIndex = 26; @@ -279,12 +283,53 @@ private void InitializeComponent() this.label7.TabIndex = 19; this.label7.Text = "i"; // + // panel4 + // + this.panel4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.panel4.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(80)))), ((int)(((byte)(80))))); + this.panel4.Controls.Add(this.label2); + this.panel4.Location = new System.Drawing.Point(417, 167); + this.panel4.Name = "panel4"; + this.panel4.Size = new System.Drawing.Size(54, 35); + this.panel4.TabIndex = 29; + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.label2.AutoSize = true; + this.label2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(80)))), ((int)(((byte)(80))))); + this.label2.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.label2.Font = new System.Drawing.Font("Wingdings", 20.25F); + this.label2.Location = new System.Drawing.Point(10, 3); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(37, 30); + this.label2.TabIndex = 17; + this.label2.Text = "Þ"; + // + // cmdFlutterInstall + // + this.cmdFlutterInstall.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.cmdFlutterInstall.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(80)))), ((int)(((byte)(80))))); + this.cmdFlutterInstall.FlatAppearance.BorderSize = 0; + this.cmdFlutterInstall.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.cmdFlutterInstall.Font = new System.Drawing.Font("Segoe UI", 11F); + this.cmdFlutterInstall.Location = new System.Drawing.Point(417, 166); + this.cmdFlutterInstall.Name = "cmdFlutterInstall"; + this.cmdFlutterInstall.Size = new System.Drawing.Size(159, 37); + this.cmdFlutterInstall.TabIndex = 28; + this.cmdFlutterInstall.Text = "Install Flutter"; + this.cmdFlutterInstall.TextAlign = System.Drawing.ContentAlignment.BottomRight; + this.cmdFlutterInstall.UseVisualStyleBackColor = false; + this.cmdFlutterInstall.Click += new System.EventHandler(this.cmdFlutterInstall_Click); + // // StartupForm // this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(70)))), ((int)(((byte)(73))))); this.ClientSize = new System.Drawing.Size(592, 296); + this.Controls.Add(this.panel4); + this.Controls.Add(this.cmdFlutterInstall); this.Controls.Add(this.panel5); this.Controls.Add(this.panel3); this.Controls.Add(this.panel2); @@ -316,6 +361,8 @@ private void InitializeComponent() this.panel3.PerformLayout(); this.panel5.ResumeLayout(false); this.panel5.PerformLayout(); + this.panel4.ResumeLayout(false); + this.panel4.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -342,6 +389,9 @@ private void InitializeComponent() private System.Windows.Forms.Label label5; private System.Windows.Forms.Panel panel5; private System.Windows.Forms.Label label7; + private System.Windows.Forms.Panel panel4; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button cmdFlutterInstall; } } diff --git a/src/KFlearning/Views/StartupForm.cs b/src/KFlearning/Views/StartupForm.cs index 907ea00..78cfdac 100644 --- a/src/KFlearning/Views/StartupForm.cs +++ b/src/KFlearning/Views/StartupForm.cs @@ -3,6 +3,7 @@ using System.Windows.Forms; using KFlearning.Control; using KFlearning.Core.Extensions; +using KFlearning.Core.Services; using KFlearning.Models; using KFlearning.Properties; using KFlearning.Services; @@ -11,6 +12,7 @@ namespace KFlearning.Views { public partial class StartupForm : Form { + private readonly IPathManager _pathManager = Program.Container.Resolve(); private readonly IProjectService _project = Program.Container.Resolve(); private readonly IHistoryService _history = Program.Container.Resolve(); @@ -80,6 +82,18 @@ private void cmdAbout_Click(object sender, System.EventArgs e) frm.ShowDialog(this); } + private void cmdFlutterInstall_Click(object sender, EventArgs e) + { + if (_pathManager.IsFlutterInstalled) + { + MessageBox.Show("Flutter terdeteksi pada sistem. Anda tidak perlu install Flutter lagi.", Resources.AppName, MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + using (var frm = Program.Container.Resolve()) + frm.ShowDialog(this); + } + private void cmdClear_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { lstHistory.Items.Clear();