From 0a80625210eb915831b3c24c8f4ba3e5b1d1b15a Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Sat, 21 Apr 2018 07:25:52 +0900 Subject: [PATCH 01/13] =?UTF-8?q?=E3=82=B5=E3=83=A0=E3=83=8D=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E7=94=BB=E5=83=8F=E3=81=AE=E6=8B=A1=E5=A4=A7=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=82=92=E8=A1=8C=E3=81=86MediaViewerLightDialog?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTween/MediaViewerLightDialog.Designer.cs | 81 ++++++++++ OpenTween/MediaViewerLightDialog.cs | 121 +++++++++++++++ OpenTween/MediaViewerLightDialog.resx | 7 + OpenTween/Models/MediaViewerLight.cs | 151 +++++++++++++++++++ OpenTween/OpenTween.csproj | 11 ++ OpenTween/Tween.cs | 18 ++- 6 files changed, 387 insertions(+), 2 deletions(-) create mode 100644 OpenTween/MediaViewerLightDialog.Designer.cs create mode 100644 OpenTween/MediaViewerLightDialog.cs create mode 100644 OpenTween/MediaViewerLightDialog.resx create mode 100644 OpenTween/Models/MediaViewerLight.cs diff --git a/OpenTween/MediaViewerLightDialog.Designer.cs b/OpenTween/MediaViewerLightDialog.Designer.cs new file mode 100644 index 000000000..3a541e3b6 --- /dev/null +++ b/OpenTween/MediaViewerLightDialog.Designer.cs @@ -0,0 +1,81 @@ +namespace OpenTween +{ + partial class MediaViewerLightDialog + { + /// + /// 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.pictureBox = new OpenTween.OTPictureBox(); + this.progressBar = new System.Windows.Forms.ProgressBar(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).BeginInit(); + this.SuspendLayout(); + // + // pictureBox + // + this.pictureBox.AccessibleRole = System.Windows.Forms.AccessibleRole.Graphic; + this.pictureBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.pictureBox.Location = new System.Drawing.Point(0, 0); + this.pictureBox.Name = "pictureBox"; + this.pictureBox.Size = new System.Drawing.Size(500, 500); + this.pictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.pictureBox.TabIndex = 1; + this.pictureBox.TabStop = false; + // + // progressBar + // + this.progressBar.Location = new System.Drawing.Point(90, 243); + this.progressBar.MarqueeAnimationSpeed = 25; + this.progressBar.Name = "progressBar"; + this.progressBar.Size = new System.Drawing.Size(320, 15); + this.progressBar.TabIndex = 2; + // + // MediaViewerLightDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.BackColor = System.Drawing.SystemColors.ControlDark; + this.ClientSize = new System.Drawing.Size(500, 500); + this.Controls.Add(this.progressBar); + this.Controls.Add(this.pictureBox); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "MediaViewerLightDialog"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.MediaViewerLightDialog_KeyDown); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private OTPictureBox pictureBox; + private System.Windows.Forms.ProgressBar progressBar; + } +} \ No newline at end of file diff --git a/OpenTween/MediaViewerLightDialog.cs b/OpenTween/MediaViewerLightDialog.cs new file mode 100644 index 000000000..de3a18c4a --- /dev/null +++ b/OpenTween/MediaViewerLightDialog.cs @@ -0,0 +1,121 @@ +// OpenTween - Client of Twitter +// Copyright (c) 2018 kim_upsilon (@kim_upsilon) +// All rights reserved. +// +// This file is part of OpenTween. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see , or write to +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +// Boston, MA 02110-1301, USA. + +#nullable enable + +using System.ComponentModel; +using System.Windows.Forms; +using OpenTween.Models; + +namespace OpenTween +{ + public partial class MediaViewerLightDialog : OTBaseForm + { + private readonly MediaViewerLight model; + + public MediaViewerLightDialog(MediaViewerLight model) + { + this.InitializeComponent(); + + this.model = model; + this.model.PropertyChanged += + (s, e) => this.InvokeAsync(() => this.Model_PropertyChanged(s, e)); + + this.UpdateAll(); + } + + private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(MediaViewerLight.LoadState): + this.UpdateLoadState(); + break; + case nameof(MediaViewerLight.ImageSize): + case nameof(MediaViewerLight.ReceivedSize): + this.UpdateLoadProgress(); + break; + case nameof(MediaViewerLight.Image): + this.UpdateImage(); + break; + case "": + case null: + this.UpdateAll(); + break; + default: + break; + } + } + + private void UpdateAll() + { + this.UpdateLoadState(); + this.UpdateLoadProgress(); + this.UpdateImage(); + } + + private void UpdateLoadState() + { + switch (this.model.LoadState) + { + case MediaViewerLight.LoadStateEnum.BeforeLoad: + case MediaViewerLight.LoadStateEnum.HeaderArrived: + this.progressBar.Visible = true; + this.pictureBox.Visible = false; + break; + case MediaViewerLight.LoadStateEnum.LoadSuccessed: + this.progressBar.Visible = false; + this.pictureBox.Visible = true; + break; + case MediaViewerLight.LoadStateEnum.LoadError: + this.progressBar.Visible = false; + this.pictureBox.Visible = true; + this.pictureBox.ShowErrorImage(); + break; + default: + break; + } + } + + private void UpdateLoadProgress() + { + if (this.model.ImageSize != null && this.model.ReceivedSize != null) + { + this.progressBar.Maximum = (int?)this.model.ImageSize ?? 0; + this.progressBar.Value = (int?)this.model.ReceivedSize ?? 0; + this.progressBar.Style = ProgressBarStyle.Continuous; + } + else + { + this.progressBar.Style = ProgressBarStyle.Marquee; + } + } + + private void UpdateImage() + => this.pictureBox.Image = this.model.Image; + + private void MediaViewerLightDialog_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyData == Keys.Escape) + this.Close(); + } + } +} diff --git a/OpenTween/MediaViewerLightDialog.resx b/OpenTween/MediaViewerLightDialog.resx new file mode 100644 index 000000000..cabc33e77 --- /dev/null +++ b/OpenTween/MediaViewerLightDialog.resx @@ -0,0 +1,7 @@ + + 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 + + diff --git a/OpenTween/Models/MediaViewerLight.cs b/OpenTween/Models/MediaViewerLight.cs new file mode 100644 index 000000000..2eeeb3cb2 --- /dev/null +++ b/OpenTween/Models/MediaViewerLight.cs @@ -0,0 +1,151 @@ +// OpenTween - Client of Twitter +// Copyright (c) 2018 kim_upsilon (@kim_upsilon) +// All rights reserved. +// +// This file is part of OpenTween. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see , or write to +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +// Boston, MA 02110-1301, USA. + +#nullable enable + +using System; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using OpenTween.Connection; +using OpenTween.Thumbnail; + +namespace OpenTween.Models +{ + public sealed class MediaViewerLight : NotifyPropertyChangedBase, IDisposable + { + private string? imageUrl; + private MemoryImage? image; + private LoadStateEnum loadState; + private long? imageSize; + private long? receivedSize; + + public string? ImageUrl + { + get => this.imageUrl; + set => this.SetProperty(ref this.imageUrl, value); + } + + public MemoryImage? Image + { + get => this.image; + private set => this.SetProperty(ref this.image, value); + } + + public LoadStateEnum LoadState + { + get => this.loadState; + private set => this.SetProperty(ref this.loadState, value); + } + + public long? ImageSize + { + get => this.imageSize; + private set => this.SetProperty(ref this.imageSize, value); + } + + public long? ReceivedSize + { + get => this.receivedSize; + private set => this.SetProperty(ref this.receivedSize, value); + } + + public enum LoadStateEnum + { + BeforeLoad = 0, + HeaderArrived = 1, + LoadSuccessed = 2, + LoadError = 3, + } + + public void SetFromThubnailInfo(ThumbnailInfo thumb) + => this.ImageUrl = thumb.FullSizeImageUrl ?? thumb.ThumbnailImageUrl; + + public async Task LoadAsync(CancellationToken cancellationToken) + { + try + { + this.ImageSize = null; + this.ReceivedSize = null; + this.LoadState = LoadStateEnum.BeforeLoad; + + using (var response = await Networking.Http.GetAsync( + this.ImageUrl, + HttpCompletionOption.ResponseHeadersRead, + cancellationToken)) + { + response.EnsureSuccessStatusCode(); + + this.ImageSize = response.Content.Headers.ContentLength; + this.ReceivedSize = 0; + this.LoadState = LoadStateEnum.HeaderArrived; + + var initialSize = (int?)this.ImageSize ?? 2 * 1024 * 1024; + using var memstream = new MemoryStream(initialSize); + + using (var responseStream = await response.Content.ReadAsStreamAsync()) + { + var bufferSize = 1 * 1024 * 1024; + var buffer = new byte[bufferSize]; + + int received; + while ((received = await responseStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) != 0) + { + memstream.Write(buffer, 0, received); + + this.ReceivedSize += received; + } + } + + memstream.Position = 0; + this.Image = MemoryImage.CopyFromStream(memstream); + } + + this.LoadState = LoadStateEnum.LoadSuccessed; + } + catch (Exception) + { + this.LoadState = LoadStateEnum.LoadError; + + try + { + throw; + } + catch (HttpRequestException) + { + } + catch (InvalidImageException) + { + } + catch (OperationCanceledException) + { + } + catch (IOException) + { + } + } + } + + public void Dispose() + => this.Image?.Dispose(); + } +} diff --git a/OpenTween/OpenTween.csproj b/OpenTween/OpenTween.csproj index 16fd1c0d9..0ebeafa1d 100644 --- a/OpenTween/OpenTween.csproj +++ b/OpenTween/OpenTween.csproj @@ -149,6 +149,12 @@ FilterDialog.cs + + Form + + + MediaViewerLightDialog.cs + Form @@ -162,6 +168,7 @@ + @@ -488,6 +495,10 @@ HashtagManage.cs + + MediaViewerLightDialog.cs + Designer + InputDialog.cs diff --git a/OpenTween/Tween.cs b/OpenTween/Tween.cs index 80d6d5289..2490840b4 100644 --- a/OpenTween/Tween.cs +++ b/OpenTween/Tween.cs @@ -11217,9 +11217,23 @@ private async void TweetThumbnail_ThumbnailImageSearchClick(object sender, Thumb private async Task OpenThumbnailPicture(ThumbnailInfo thumbnail) { - var url = thumbnail.FullSizeImageUrl ?? thumbnail.MediaPageUrl; + var imageUrl = thumbnail.FullSizeImageUrl ?? thumbnail.ThumbnailImageUrl; - await MyCommon.OpenInBrowserAsync(this, url); + if (imageUrl != null) + { + using (var viewer = new MediaViewerLight()) + using (var viewerDialog = new MediaViewerLightDialog(viewer)) + { + viewer.ImageUrl = imageUrl; + var loadTask = Task.Run(() => viewer.LoadAsync(CancellationToken.None)); + viewerDialog.ShowDialog(this); + await loadTask; + } + } + else + { + await MyCommon.OpenInBrowserAsync(this, thumbnail.MediaPageUrl); + } } private async void TwitterApiStatusToolStripMenuItem_Click(object sender, EventArgs e) From 950604f9df571da4c1958d6a9f3d40e3a03ff405 Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Tue, 24 Apr 2018 09:02:29 +0900 Subject: [PATCH 02/13] =?UTF-8?q?MediaViewerLightDialog=E3=81=8C=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=83=89=E4=B8=AD=E3=81=AB=E9=96=89=E3=81=98=E3=82=89?= =?UTF-8?q?=E3=82=8C=E3=81=9F=E5=A0=B4=E5=90=88=E3=81=AF=E7=94=BB=E5=83=8F?= =?UTF-8?q?=E3=81=AE=E8=AA=AD=E3=81=BF=E8=BE=BC=E3=81=BF=E3=82=92=E3=82=AD?= =?UTF-8?q?=E3=83=A3=E3=83=B3=E3=82=BB=E3=83=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTween/MediaViewerLightDialog.Designer.cs | 1 + OpenTween/MediaViewerLightDialog.cs | 3 ++ OpenTween/Models/MediaViewerLight.cs | 36 ++++++++++++++++++-- OpenTween/Tween.cs | 3 +- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/OpenTween/MediaViewerLightDialog.Designer.cs b/OpenTween/MediaViewerLightDialog.Designer.cs index 3a541e3b6..85e7a819b 100644 --- a/OpenTween/MediaViewerLightDialog.Designer.cs +++ b/OpenTween/MediaViewerLightDialog.Designer.cs @@ -67,6 +67,7 @@ private void InitializeComponent() this.ShowInTaskbar = false; this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MediaViewerLightDialog_FormClosing); this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.MediaViewerLightDialog_KeyDown); ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).EndInit(); this.ResumeLayout(false); diff --git a/OpenTween/MediaViewerLightDialog.cs b/OpenTween/MediaViewerLightDialog.cs index de3a18c4a..e5eb589d2 100644 --- a/OpenTween/MediaViewerLightDialog.cs +++ b/OpenTween/MediaViewerLightDialog.cs @@ -117,5 +117,8 @@ private void MediaViewerLightDialog_KeyDown(object sender, KeyEventArgs e) if (e.KeyData == Keys.Escape) this.Close(); } + + private void MediaViewerLightDialog_FormClosing(object sender, FormClosingEventArgs e) + => this.model.AbortLoad(); } } diff --git a/OpenTween/Models/MediaViewerLight.cs b/OpenTween/Models/MediaViewerLight.cs index 2eeeb3cb2..da5fcedca 100644 --- a/OpenTween/Models/MediaViewerLight.cs +++ b/OpenTween/Models/MediaViewerLight.cs @@ -42,7 +42,7 @@ public sealed class MediaViewerLight : NotifyPropertyChangedBase, IDisposable public string? ImageUrl { get => this.imageUrl; - set => this.SetProperty(ref this.imageUrl, value); + private set => this.SetProperty(ref this.imageUrl, value); } public MemoryImage? Image @@ -69,6 +69,8 @@ public long? ReceivedSize private set => this.SetProperty(ref this.receivedSize, value); } + private CancellationTokenSource? cts; + public enum LoadStateEnum { BeforeLoad = 0, @@ -80,10 +82,25 @@ public enum LoadStateEnum public void SetFromThubnailInfo(ThumbnailInfo thumb) => this.ImageUrl = thumb.FullSizeImageUrl ?? thumb.ThumbnailImageUrl; - public async Task LoadAsync(CancellationToken cancellationToken) + public async Task LoadAsync(string imageUrl) + { + var newCts = new CancellationTokenSource(); + var oldCts = Interlocked.Exchange(ref this.cts, newCts); + if (oldCts != null) + { + oldCts.Cancel(); + oldCts.Dispose(); + } + + await this.LoadAsync(imageUrl, newCts.Token); + } + + internal async Task LoadAsync(string imageUrl, CancellationToken cancellationToken) { try { + this.ImageUrl = imageUrl; + this.Image = null; this.ImageSize = null; this.ReceivedSize = null; this.LoadState = LoadStateEnum.BeforeLoad; @@ -145,7 +162,20 @@ public async Task LoadAsync(CancellationToken cancellationToken) } } + public void AbortLoad() + { + var oldCts = Interlocked.Exchange(ref this.cts, null); + if (oldCts != null) + { + oldCts.Cancel(); + oldCts.Dispose(); + } + } + public void Dispose() - => this.Image?.Dispose(); + { + this.cts?.Dispose(); + this.Image?.Dispose(); + } } } diff --git a/OpenTween/Tween.cs b/OpenTween/Tween.cs index 2490840b4..204bd98dd 100644 --- a/OpenTween/Tween.cs +++ b/OpenTween/Tween.cs @@ -11224,8 +11224,7 @@ private async Task OpenThumbnailPicture(ThumbnailInfo thumbnail) using (var viewer = new MediaViewerLight()) using (var viewerDialog = new MediaViewerLightDialog(viewer)) { - viewer.ImageUrl = imageUrl; - var loadTask = Task.Run(() => viewer.LoadAsync(CancellationToken.None)); + var loadTask = Task.Run(() => viewer.LoadAsync(imageUrl)); viewerDialog.ShowDialog(this); await loadTask; } From 2638368ec8fb9c0d23e5d4813e52bf7805ed4f6b Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Sat, 28 Apr 2018 06:51:00 +0900 Subject: [PATCH 03/13] =?UTF-8?q?=E7=94=BB=E5=83=8F=E3=81=AE=E6=8B=A1?= =?UTF-8?q?=E5=A4=A7=E8=A1=A8=E7=A4=BA=E4=B8=AD=E3=81=AB=E7=9F=A2=E5=8D=B0?= =?UTF-8?q?=E3=82=AD=E3=83=BC=E3=81=8C=E6=8A=BC=E3=81=95=E3=82=8C=E3=81=9F?= =?UTF-8?q?=E5=A0=B4=E5=90=88=E3=81=AF=E5=89=8D=E5=BE=8C=E3=81=AE=E7=94=BB?= =?UTF-8?q?=E5=83=8F=E3=81=AB=E5=88=87=E3=82=8A=E6=9B=BF=E3=81=88=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTween/MediaViewerLightDialog.cs | 20 +++++- OpenTween/Models/MediaViewerLight.cs | 97 ++++++++++++++++++++++++++-- OpenTween/Tween.cs | 29 ++++----- OpenTween/TweetThumbnail.cs | 8 ++- 4 files changed, 129 insertions(+), 25 deletions(-) diff --git a/OpenTween/MediaViewerLightDialog.cs b/OpenTween/MediaViewerLightDialog.cs index e5eb589d2..0126ae744 100644 --- a/OpenTween/MediaViewerLightDialog.cs +++ b/OpenTween/MediaViewerLightDialog.cs @@ -112,10 +112,24 @@ private void UpdateLoadProgress() private void UpdateImage() => this.pictureBox.Image = this.model.Image; - private void MediaViewerLightDialog_KeyDown(object sender, KeyEventArgs e) + private async void MediaViewerLightDialog_KeyDown(object sender, KeyEventArgs e) { - if (e.KeyData == Keys.Escape) - this.Close(); + switch (e.KeyData) + { + case Keys.Up: + case Keys.Left: + await this.model.SelectPreviousMedia(); + break; + case Keys.Down: + case Keys.Right: + await this.model.SelectNextMedia(); + break; + case Keys.Escape: + this.Close(); + break; + default: + break; + } } private void MediaViewerLightDialog_FormClosing(object sender, FormClosingEventArgs e) diff --git a/OpenTween/Models/MediaViewerLight.cs b/OpenTween/Models/MediaViewerLight.cs index da5fcedca..115c1964b 100644 --- a/OpenTween/Models/MediaViewerLight.cs +++ b/OpenTween/Models/MediaViewerLight.cs @@ -33,12 +33,26 @@ namespace OpenTween.Models { public sealed class MediaViewerLight : NotifyPropertyChangedBase, IDisposable { + private ThumbnailInfo[] mediaItems = Array.Empty(); + private int displayMediaIndex; private string? imageUrl; private MemoryImage? image; private LoadStateEnum loadState; private long? imageSize; private long? receivedSize; + public ThumbnailInfo[] MediaItems + { + get => this.mediaItems; + private set => this.SetProperty(ref this.mediaItems, value); + } + + public int DisplayMediaIndex + { + get => this.displayMediaIndex; + private set => this.SetProperty(ref this.displayMediaIndex, value); + } + public string? ImageUrl { get => this.imageUrl; @@ -79,10 +93,39 @@ public enum LoadStateEnum LoadError = 3, } - public void SetFromThubnailInfo(ThumbnailInfo thumb) - => this.ImageUrl = thumb.FullSizeImageUrl ?? thumb.ThumbnailImageUrl; + public void SetMediaItems(ThumbnailInfo[] thumbnails) + { + this.DisplayMediaIndex = 0; + this.MediaItems = thumbnails; + } - public async Task LoadAsync(string imageUrl) + public async Task SelectMedia(int displayIndex) + { + this.DisplayMediaIndex = displayIndex; + + var media = this.MediaItems[displayIndex]; + await this.LoadAsync(media); + } + + public async Task SelectPreviousMedia() + { + var currentIndex = this.DisplayMediaIndex; + if (currentIndex == 0) + return; + + await this.SelectMedia(currentIndex - 1); + } + + public async Task SelectNextMedia() + { + var currentIndex = this.DisplayMediaIndex; + if (currentIndex == this.MediaItems.Length - 1) + return; + + await this.SelectMedia(currentIndex + 1); + } + + internal async Task LoadAsync(ThumbnailInfo media) { var newCts = new CancellationTokenSource(); var oldCts = Interlocked.Exchange(ref this.cts, newCts); @@ -92,7 +135,15 @@ public async Task LoadAsync(string imageUrl) oldCts.Dispose(); } - await this.LoadAsync(imageUrl, newCts.Token); + var imageUrl = media.FullSizeImageUrl ?? media.ThumbnailImageUrl; + if (imageUrl != null) + { + await this.LoadAsync(imageUrl, newCts.Token); + } + else + { + await this.LoadAsync(() => media.LoadThumbnailImageAsync(newCts.Token)); + } } internal async Task LoadAsync(string imageUrl, CancellationToken cancellationToken) @@ -162,6 +213,44 @@ internal async Task LoadAsync(string imageUrl, CancellationToken cancellationTok } } + internal async Task LoadAsync(Func> imageTaskFunc) + { + try + { + this.ImageUrl = null; + this.Image = null; + this.ImageSize = null; + this.ReceivedSize = null; + this.LoadState = LoadStateEnum.BeforeLoad; + + var image = await imageTaskFunc(); + + this.Image = image; + this.LoadState = LoadStateEnum.LoadSuccessed; + } + catch (Exception) + { + this.LoadState = LoadStateEnum.LoadError; + + try + { + throw; + } + catch (HttpRequestException) + { + } + catch (InvalidImageException) + { + } + catch (OperationCanceledException) + { + } + catch (IOException) + { + } + } + } + public void AbortLoad() { var oldCts = Interlocked.Exchange(ref this.cts, null); diff --git a/OpenTween/Tween.cs b/OpenTween/Tween.cs index 204bd98dd..8c362b295 100644 --- a/OpenTween/Tween.cs +++ b/OpenTween/Tween.cs @@ -6209,7 +6209,7 @@ private void InitializeShortcuts() ShortcutCommand.Create(Keys.Alt | Keys.Shift | Keys.Enter) .FocusedOn(FocusedControl.ListTab) .OnlyWhen(() => !this.SplitContainer3.Panel2Collapsed) - .Do(() => this.OpenThumbnailPicture(this.tweetThumbnail1.Thumbnail)), + .Do(() => this.OpenMediaViewer()), }; } @@ -11210,28 +11210,23 @@ private void TweetThumbnail_ThumbnailLoading(object sender, EventArgs e) => this.SplitContainer3.Panel2Collapsed = false; private async void TweetThumbnail_ThumbnailDoubleClick(object sender, ThumbnailDoubleClickEventArgs e) - => await this.OpenThumbnailPicture(e.Thumbnail); + => await this.OpenMediaViewer(); private async void TweetThumbnail_ThumbnailImageSearchClick(object sender, ThumbnailImageSearchEventArgs e) => await MyCommon.OpenInBrowserAsync(this, e.ImageUrl); - private async Task OpenThumbnailPicture(ThumbnailInfo thumbnail) - { - var imageUrl = thumbnail.FullSizeImageUrl ?? thumbnail.ThumbnailImageUrl; + private Task OpenMediaViewer() + => this.OpenMediaViewer(this.tweetThumbnail1.Thumbnails, this.tweetThumbnail1.DisplayThumbnailIndex); - if (imageUrl != null) - { - using (var viewer = new MediaViewerLight()) - using (var viewerDialog = new MediaViewerLightDialog(viewer)) - { - var loadTask = Task.Run(() => viewer.LoadAsync(imageUrl)); - viewerDialog.ShowDialog(this); - await loadTask; - } - } - else + private async Task OpenMediaViewer(ThumbnailInfo[] thumbnails, int displayIndex) + { + using (var viewer = new MediaViewerLight()) + using (var viewerDialog = new MediaViewerLightDialog(viewer)) { - await MyCommon.OpenInBrowserAsync(this, thumbnail.MediaPageUrl); + viewer.SetMediaItems(thumbnails); + var loadTask = Task.Run(() => viewer.SelectMedia(displayIndex)); + viewerDialog.ShowDialog(this); + await loadTask; } } diff --git a/OpenTween/TweetThumbnail.cs b/OpenTween/TweetThumbnail.cs index 6d6b00c1a..7db307906 100644 --- a/OpenTween/TweetThumbnail.cs +++ b/OpenTween/TweetThumbnail.cs @@ -52,8 +52,14 @@ public partial class TweetThumbnail : UserControl public event EventHandler? ThumbnailImageSearchClick; + public int DisplayThumbnailIndex + => this.scrollBar.Value; + + public ThumbnailInfo[] Thumbnails + => this.PictureBox.Select(x => x.Tag).Cast().ToArray(); + public ThumbnailInfo Thumbnail - => (ThumbnailInfo)this.PictureBox[this.scrollBar.Value].Tag; + => (ThumbnailInfo)this.PictureBox[this.DisplayThumbnailIndex].Tag; public TweetThumbnail() => this.InitializeComponent(); From 937825415a90f560c62b538d93c44c51937a1154 Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Sat, 28 Apr 2018 07:18:07 +0900 Subject: [PATCH 04/13] =?UTF-8?q?=E7=94=BB=E5=83=8F=E3=81=AE=E6=8B=A1?= =?UTF-8?q?=E5=A4=A7=E8=A1=A8=E7=A4=BA=E6=99=82=E3=81=AB=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E4=B8=AD=E3=81=AE=E7=94=BB=E5=83=8F=E3=81=AE=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=83=87=E3=83=83=E3=82=AF=E3=82=B9=E3=82=92=E3=83=80=E3=82=A4?= =?UTF-8?q?=E3=82=A2=E3=83=AD=E3=82=B0=E3=81=AE=E3=82=BF=E3=82=A4=E3=83=88?= =?UTF-8?q?=E3=83=AB=E3=81=AB=E8=A8=AD=E5=AE=9A=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTween/MediaViewerLightDialog.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/OpenTween/MediaViewerLightDialog.cs b/OpenTween/MediaViewerLightDialog.cs index 0126ae744..606f73294 100644 --- a/OpenTween/MediaViewerLightDialog.cs +++ b/OpenTween/MediaViewerLightDialog.cs @@ -46,6 +46,10 @@ private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { + case nameof(MediaViewerLight.MediaItems): + case nameof(MediaViewerLight.DisplayMediaIndex): + this.UpdateTitle(); + break; case nameof(MediaViewerLight.LoadState): this.UpdateLoadState(); break; @@ -67,11 +71,25 @@ private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e) private void UpdateAll() { + this.UpdateTitle(); this.UpdateLoadState(); this.UpdateLoadProgress(); this.UpdateImage(); } + private void UpdateTitle() + { + const string TITLE_TEMPLATE = "{0}/{1}"; + + var mediaCount = this.model.MediaItems.Length; + var displayIndex = this.model.DisplayMediaIndex; + + if (mediaCount == 1) + this.Text = ""; + else + this.Text = string.Format(TITLE_TEMPLATE, displayIndex + 1, mediaCount); + } + private void UpdateLoadState() { switch (this.model.LoadState) From 52958cc8e765ce24811e54e18c230304f5506893 Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Sat, 28 Apr 2018 07:46:57 +0900 Subject: [PATCH 05/13] =?UTF-8?q?=E7=94=BB=E5=83=8F=E3=81=AE=E6=8B=A1?= =?UTF-8?q?=E5=A4=A7=E8=A1=A8=E7=A4=BA=E4=B8=AD=E3=81=ABEnter=E3=82=AD?= =?UTF-8?q?=E3=83=BC=E3=81=8C=E6=8A=BC=E3=81=95=E3=82=8C=E3=81=9F=E3=82=89?= =?UTF-8?q?=E3=83=96=E3=83=A9=E3=82=A6=E3=82=B6=E3=82=92=E8=B5=B7=E5=8B=95?= =?UTF-8?q?=E3=81=97=E3=81=A6=E7=94=BB=E5=83=8F=E3=82=92=E9=96=8B=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTween/MediaViewerLightDialog.cs | 9 +++++++++ OpenTween/Models/MediaViewerLight.cs | 3 +++ OpenTween/Tween.cs | 3 +++ 3 files changed, 15 insertions(+) diff --git a/OpenTween/MediaViewerLightDialog.cs b/OpenTween/MediaViewerLightDialog.cs index 606f73294..88ec7e3ca 100644 --- a/OpenTween/MediaViewerLightDialog.cs +++ b/OpenTween/MediaViewerLightDialog.cs @@ -21,7 +21,9 @@ #nullable enable +using System; using System.ComponentModel; +using System.Threading.Tasks; using System.Windows.Forms; using OpenTween.Models; @@ -29,6 +31,8 @@ namespace OpenTween { public partial class MediaViewerLightDialog : OTBaseForm { + public Func? OpenInBrowser; + private readonly MediaViewerLight model; public MediaViewerLightDialog(MediaViewerLight model) @@ -142,6 +146,11 @@ private async void MediaViewerLightDialog_KeyDown(object sender, KeyEventArgs e) case Keys.Right: await this.model.SelectNextMedia(); break; + case Keys.Enter: + this.Close(); + if (this.OpenInBrowser != null) + await this.OpenInBrowser(this, this.model.DisplayMedia.MediaPageUrl); + break; case Keys.Escape: this.Close(); break; diff --git a/OpenTween/Models/MediaViewerLight.cs b/OpenTween/Models/MediaViewerLight.cs index 115c1964b..38bf9baf7 100644 --- a/OpenTween/Models/MediaViewerLight.cs +++ b/OpenTween/Models/MediaViewerLight.cs @@ -53,6 +53,9 @@ public int DisplayMediaIndex private set => this.SetProperty(ref this.displayMediaIndex, value); } + public ThumbnailInfo DisplayMedia + => this.MediaItems[this.DisplayMediaIndex]; + public string? ImageUrl { get => this.imageUrl; diff --git a/OpenTween/Tween.cs b/OpenTween/Tween.cs index 8c362b295..837135e48 100644 --- a/OpenTween/Tween.cs +++ b/OpenTween/Tween.cs @@ -11225,7 +11225,10 @@ private async Task OpenMediaViewer(ThumbnailInfo[] thumbnails, int displayIndex) { viewer.SetMediaItems(thumbnails); var loadTask = Task.Run(() => viewer.SelectMedia(displayIndex)); + + viewerDialog.OpenInBrowser = (owner, url) => MyCommon.OpenInBrowserAsync(owner, url); viewerDialog.ShowDialog(this); + await loadTask; } } From e1f534539ce118afcc62eef0d285c9bf1b9d472f Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Sat, 28 Apr 2018 08:14:29 +0900 Subject: [PATCH 06/13] =?UTF-8?q?=E7=94=BB=E5=83=8F=E3=81=AE=E8=A9=B3?= =?UTF-8?q?=E7=B4=B0=E8=A1=A8=E7=A4=BA=E3=81=AE=E7=A8=AE=E9=A1=9E=E3=82=92?= =?UTF-8?q?=20SettingLocal.xml=20=E3=81=A7=E8=A8=AD=E5=AE=9A=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTween/MediaHandler.cs | 84 +++++++++++++++++++++++++++++++ OpenTween/OpenTween.csproj | 1 + OpenTween/Setting/SettingLocal.cs | 5 ++ OpenTween/Tween.cs | 14 ++---- 4 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 OpenTween/MediaHandler.cs diff --git a/OpenTween/MediaHandler.cs b/OpenTween/MediaHandler.cs new file mode 100644 index 000000000..31d60d284 --- /dev/null +++ b/OpenTween/MediaHandler.cs @@ -0,0 +1,84 @@ +// OpenTween - Client of Twitter +// Copyright (c) 2018 kim_upsilon (@kim_upsilon) +// All rights reserved. +// +// This file is part of OpenTween. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see , or write to +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +// Boston, MA 02110-1301, USA. + +#nullable enable + +using System; +using System.ComponentModel; +using System.Threading.Tasks; +using System.Windows.Forms; +using OpenTween.Models; +using OpenTween.Thumbnail; + +namespace OpenTween +{ + public enum MediaHandlerType + { + /// 外部ブラウザで開く + ExternalBrowser, + + /// 軽量ビューアーで開く + LightViewer, + } + + public class MediaHandler + { + public MediaHandlerType MediaHandlerType { get; set; } + + public Func? OpenInBrowser { get; set; } + + public async Task OpenMediaViewer(IWin32Window owner, ThumbnailInfo[] thumbnails, int displayIndex) + { + switch (this.MediaHandlerType) + { + case MediaHandlerType.ExternalBrowser: + await this.OpenMediaInExternalBrowser(owner, thumbnails, displayIndex); + break; + case MediaHandlerType.LightViewer: + await this.OpenMediaInLightViewer(owner, thumbnails, displayIndex); + break; + default: + throw new InvalidEnumArgumentException(); + } + } + + public async Task OpenMediaInExternalBrowser(IWin32Window owner, ThumbnailInfo[] thumbnails, int displayIndex) + { + var mediaUrl = thumbnails[displayIndex].MediaPageUrl; + if (this.OpenInBrowser != null) + await this.OpenInBrowser(owner, mediaUrl); + } + + public async Task OpenMediaInLightViewer(IWin32Window owner, ThumbnailInfo[] thumbnails, int displayIndex) + { + using var viewer = new MediaViewerLight(); + using var viewerDialog = new MediaViewerLightDialog(viewer); + + viewer.SetMediaItems(thumbnails); + var loadTask = Task.Run(() => viewer.SelectMedia(displayIndex)); + + viewerDialog.OpenInBrowser = this.OpenInBrowser; + viewerDialog.ShowDialog(owner); + + await loadTask; + } + } +} diff --git a/OpenTween/OpenTween.csproj b/OpenTween/OpenTween.csproj index 0ebeafa1d..557c4d068 100644 --- a/OpenTween/OpenTween.csproj +++ b/OpenTween/OpenTween.csproj @@ -149,6 +149,7 @@ FilterDialog.cs + Form diff --git a/OpenTween/Setting/SettingLocal.cs b/OpenTween/Setting/SettingLocal.cs index 872fbbaa3..811c05f1b 100644 --- a/OpenTween/Setting/SettingLocal.cs +++ b/OpenTween/Setting/SettingLocal.cs @@ -340,6 +340,11 @@ public string EncryptProxyPassword /// public bool UseTwemoji = true; + /// + /// ツイートに添付された画像の詳細表示方法 + /// + public MediaHandlerType MediaHanderType { get; set; } = MediaHandlerType.LightViewer; + [XmlIgnore] private readonly FontConverter fontConverter = new FontConverter(); diff --git a/OpenTween/Tween.cs b/OpenTween/Tween.cs index 837135e48..d38fc6ff1 100644 --- a/OpenTween/Tween.cs +++ b/OpenTween/Tween.cs @@ -11220,17 +11220,13 @@ private Task OpenMediaViewer() private async Task OpenMediaViewer(ThumbnailInfo[] thumbnails, int displayIndex) { - using (var viewer = new MediaViewerLight()) - using (var viewerDialog = new MediaViewerLightDialog(viewer)) + var handler = new MediaHandler { - viewer.SetMediaItems(thumbnails); - var loadTask = Task.Run(() => viewer.SelectMedia(displayIndex)); - - viewerDialog.OpenInBrowser = (owner, url) => MyCommon.OpenInBrowserAsync(owner, url); - viewerDialog.ShowDialog(this); + MediaHandlerType = SettingManager.Local.MediaHanderType, + OpenInBrowser = (owner, url) => MyCommon.OpenInBrowserAsync(owner, url), + }; - await loadTask; - } + await handler.OpenMediaViewer(this, thumbnails, displayIndex); } private async void TwitterApiStatusToolStripMenuItem_Click(object sender, EventArgs e) From 5be8238729cc6f10397af252323676224e6daecd Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Mon, 30 Apr 2018 04:13:37 +0900 Subject: [PATCH 07/13] =?UTF-8?q?=E7=94=BB=E5=83=8F=E3=82=92=E5=9F=8B?= =?UTF-8?q?=E3=82=81=E8=BE=BC=E3=81=BF=E3=83=96=E3=83=A9=E3=82=A6=E3=82=B6?= =?UTF-8?q?=E3=81=A7=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B=20MediaViewerWebB?= =?UTF-8?q?rowserDialog=20=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTween/MediaHandler.cs | 14 ++++ .../MediaViewerWebBrowserDialog.Designer.cs | 71 ++++++++++++++++++ OpenTween/MediaViewerWebBrowserDialog.cs | 75 +++++++++++++++++++ OpenTween/MediaViewerWebBrowserDialog.resx | 7 ++ OpenTween/OpenTween.csproj | 9 +++ 5 files changed, 176 insertions(+) create mode 100644 OpenTween/MediaViewerWebBrowserDialog.Designer.cs create mode 100644 OpenTween/MediaViewerWebBrowserDialog.cs create mode 100644 OpenTween/MediaViewerWebBrowserDialog.resx diff --git a/OpenTween/MediaHandler.cs b/OpenTween/MediaHandler.cs index 31d60d284..a6af1e02d 100644 --- a/OpenTween/MediaHandler.cs +++ b/OpenTween/MediaHandler.cs @@ -37,6 +37,9 @@ public enum MediaHandlerType /// 軽量ビューアーで開く LightViewer, + + /// 埋め込みブラウザで開く + WebBrowserViewer, } public class MediaHandler @@ -55,6 +58,9 @@ public async Task OpenMediaViewer(IWin32Window owner, ThumbnailInfo[] thumbnails case MediaHandlerType.LightViewer: await this.OpenMediaInLightViewer(owner, thumbnails, displayIndex); break; + case MediaHandlerType.WebBrowserViewer: + this.OpenMediaInWebBrowserViewer(owner, thumbnails, displayIndex); + break; default: throw new InvalidEnumArgumentException(); } @@ -80,5 +86,13 @@ public async Task OpenMediaInLightViewer(IWin32Window owner, ThumbnailInfo[] thu await loadTask; } + + public void OpenMediaInWebBrowserViewer(IWin32Window owner, ThumbnailInfo[] thumbnails, int displayIndex) + { + using var viewerDialog = new MediaViewerWebBrowserDialog(); + + viewerDialog.SetMediaItem(thumbnails[displayIndex]); + viewerDialog.ShowDialog(owner); + } } } diff --git a/OpenTween/MediaViewerWebBrowserDialog.Designer.cs b/OpenTween/MediaViewerWebBrowserDialog.Designer.cs new file mode 100644 index 000000000..90ac0014f --- /dev/null +++ b/OpenTween/MediaViewerWebBrowserDialog.Designer.cs @@ -0,0 +1,71 @@ +namespace OpenTween +{ + partial class MediaViewerWebBrowserDialog + { + /// + /// 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.webBrowser = new System.Windows.Forms.WebBrowser(); + this.SuspendLayout(); + // + // webBrowser + // + this.webBrowser.AllowNavigation = false; + this.webBrowser.AllowWebBrowserDrop = false; + this.webBrowser.Dock = System.Windows.Forms.DockStyle.Fill; + this.webBrowser.IsWebBrowserContextMenuEnabled = false; + this.webBrowser.Location = new System.Drawing.Point(0, 0); + this.webBrowser.MinimumSize = new System.Drawing.Size(20, 20); + this.webBrowser.Name = "webBrowser"; + this.webBrowser.ScriptErrorsSuppressed = true; + this.webBrowser.ScrollBarsEnabled = false; + this.webBrowser.Size = new System.Drawing.Size(500, 500); + this.webBrowser.TabIndex = 0; + this.webBrowser.WebBrowserShortcutsEnabled = false; + // + // MediaViewerWebBrowserDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.BackColor = System.Drawing.SystemColors.ControlDark; + this.ClientSize = new System.Drawing.Size(500, 500); + this.Controls.Add(this.webBrowser); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "MediaViewerWebBrowserDialog"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.WebBrowser webBrowser; + } +} \ No newline at end of file diff --git a/OpenTween/MediaViewerWebBrowserDialog.cs b/OpenTween/MediaViewerWebBrowserDialog.cs new file mode 100644 index 000000000..f67d5809c --- /dev/null +++ b/OpenTween/MediaViewerWebBrowserDialog.cs @@ -0,0 +1,75 @@ +// OpenTween - Client of Twitter +// Copyright (c) 2018 kim_upsilon (@kim_upsilon) +// All rights reserved. +// +// This file is part of OpenTween. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see , or write to +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +// Boston, MA 02110-1301, USA. + +#nullable enable + +using System; +using System.Net; +using OpenTween.Thumbnail; + +namespace OpenTween +{ + public partial class MediaViewerWebBrowserDialog : OTBaseForm + { + public MediaViewerWebBrowserDialog() + { + this.InitializeComponent(); + } + + public void SetMediaItem(ThumbnailInfo media) + => this.webBrowser.DocumentText = this.CreateDocument(media); + + internal string CreateDocument(ThumbnailInfo media) + { + const string TEMPLATE_HEAD = @" + + + +MediaViewerWebBrowserDialog + +
+
+
+"; + var bgColor = this.BackColor; + var html = TEMPLATE_HEAD + .Replace("###BG_COLOR###", $"{bgColor.R},{bgColor.G},{bgColor.B}") + .Replace("###IMAGE_URI###", WebUtility.HtmlEncode(Uri.EscapeUriString(media.FullSizeImageUrl ?? media.ThumbnailImageUrl ?? ""))); + + return html; + } + } +} diff --git a/OpenTween/MediaViewerWebBrowserDialog.resx b/OpenTween/MediaViewerWebBrowserDialog.resx new file mode 100644 index 000000000..cabc33e77 --- /dev/null +++ b/OpenTween/MediaViewerWebBrowserDialog.resx @@ -0,0 +1,7 @@ + + 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 + + diff --git a/OpenTween/OpenTween.csproj b/OpenTween/OpenTween.csproj index 557c4d068..8958542a5 100644 --- a/OpenTween/OpenTween.csproj +++ b/OpenTween/OpenTween.csproj @@ -156,6 +156,12 @@ MediaViewerLightDialog.cs + + Form + + + MediaViewerWebBrowserDialog.cs + Form @@ -500,6 +506,9 @@ MediaViewerLightDialog.cs Designer + + MediaViewerWebBrowserDialog.cs + InputDialog.cs From a0e57b14fad2e4d3c9e42f83377435d90ed719fd Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Mon, 30 Apr 2018 04:55:27 +0900 Subject: [PATCH 08/13] =?UTF-8?q?MediaViewerWebBrowserDialog=E3=81=A7?= =?UTF-8?q?=E3=81=AE=E5=8B=95=E7=94=BB=E3=81=AE=E5=86=8D=E7=94=9F=E6=A9=9F?= =?UTF-8?q?=E8=83=BD=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTween/MediaViewerWebBrowserDialog.cs | 31 ++++++++++++++++--- .../Thumbnail/Services/TwitterComVideo.cs | 3 +- OpenTween/Thumbnail/ThumbnailInfo.cs | 3 ++ OpenTween/Twitter.cs | 10 ++++-- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/OpenTween/MediaViewerWebBrowserDialog.cs b/OpenTween/MediaViewerWebBrowserDialog.cs index f67d5809c..ae9318d7e 100644 --- a/OpenTween/MediaViewerWebBrowserDialog.cs +++ b/OpenTween/MediaViewerWebBrowserDialog.cs @@ -59,15 +59,38 @@ internal string CreateDocument(ThumbnailInfo media) background-position: center; background-repeat: no-repeat; } +.media-video { + width: 100%; + height: 100%; +} +"; + var bgColor = this.BackColor; + var html = TEMPLATE_HEAD + .Replace("###BG_COLOR###", $"{bgColor.R},{bgColor.G},{bgColor.B}"); + + if (media.VideoUrl != null) + { + const string TEMPLATE_VIDEO_BODY = @" +
+ +
+"; + html += TEMPLATE_VIDEO_BODY + .Replace("###VIDEO_URI###", WebUtility.HtmlEncode(media.VideoUrl)); + } + else + { + const string TEMPLATE_IMAGE_BODY = @"
"; - var bgColor = this.BackColor; - var html = TEMPLATE_HEAD - .Replace("###BG_COLOR###", $"{bgColor.R},{bgColor.G},{bgColor.B}") - .Replace("###IMAGE_URI###", WebUtility.HtmlEncode(Uri.EscapeUriString(media.FullSizeImageUrl ?? media.ThumbnailImageUrl ?? ""))); + html += TEMPLATE_IMAGE_BODY + .Replace("###IMAGE_URI###", WebUtility.HtmlEncode(Uri.EscapeUriString(media.FullSizeImageUrl ?? media.ThumbnailImageUrl ?? ""))); + } return html; } diff --git a/OpenTween/Thumbnail/Services/TwitterComVideo.cs b/OpenTween/Thumbnail/Services/TwitterComVideo.cs index 881bd5d80..72984a399 100644 --- a/OpenTween/Thumbnail/Services/TwitterComVideo.cs +++ b/OpenTween/Thumbnail/Services/TwitterComVideo.cs @@ -58,8 +58,9 @@ public TwitterComVideo(HttpClient? http) { return new ThumbnailInfo { - MediaPageUrl = mediaInfo.VideoUrl, + MediaPageUrl = MyCommon.GetStatusUrl(post.ScreenName, post.StatusId), ThumbnailImageUrl = url, + VideoUrl = mediaInfo.VideoUrl, TooltipText = mediaInfo.AltText, IsPlayable = true, }; diff --git a/OpenTween/Thumbnail/ThumbnailInfo.cs b/OpenTween/Thumbnail/ThumbnailInfo.cs index 4dfe2010f..1d4c747b9 100644 --- a/OpenTween/Thumbnail/ThumbnailInfo.cs +++ b/OpenTween/Thumbnail/ThumbnailInfo.cs @@ -54,6 +54,9 @@ public class ThumbnailInfo : IEquatable /// public string? FullSizeImageUrl { get; set; } + /// 動画 URL (video/mp4 形式のみ) + public string? VideoUrl { get; set; } + /// ツールチップとして表示するテキスト /// /// サムネイル画像にマウスオーバーした際に表示されるテキスト diff --git a/OpenTween/Twitter.cs b/OpenTween/Twitter.cs index 6638b1e35..174120acf 100644 --- a/OpenTween/Twitter.cs +++ b/OpenTween/Twitter.cs @@ -1534,10 +1534,14 @@ private void ExtractEntities(TwitterEntities? entities, List<(long UserId, strin { if (!media.Any(x => x.Url == ent.MediaUrlHttps)) { - if (ent.VideoInfo != null && - ent.Type == "animated_gif" || ent.Type == "video") + if (ent.VideoInfo != null && (ent.Type == "animated_gif" || ent.Type == "video")) { - media.Add(new MediaInfo(ent.MediaUrlHttps, ent.AltText, ent.ExpandedUrl)); + var videoUrl = ent.VideoInfo.Variants + .Where(v => v.ContentType == "video/mp4") + .OrderByDescending(v => v.Bitrate) + .Select(v => v.Url).FirstOrDefault(); + + media.Add(new MediaInfo(ent.MediaUrlHttps, ent.AltText, videoUrl)); } else { From 0e5b70dbafd3e5ef1b13138643450fa47e46a692 Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Tue, 1 May 2018 05:08:44 +0900 Subject: [PATCH 09/13] =?UTF-8?q?=E5=9F=8B=E3=82=81=E8=BE=BC=E3=81=BF?= =?UTF-8?q?=E3=83=96=E3=83=A9=E3=82=A6=E3=82=B6=E3=81=AB=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=99=E3=82=8BHTML=E3=81=AE=E7=94=9F=E6=88=90=E3=82=92?= =?UTF-8?q?=E5=88=A5=E3=82=AF=E3=83=A9=E3=82=B9=E3=81=AB=E5=88=86=E9=9B=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTween/MediaHandler.cs | 5 +- OpenTween/MediaViewerWebBrowserDialog.cs | 90 ++++++---------- OpenTween/Models/ColorRGB.cs | 44 ++++++++ OpenTween/Models/MediaViewerWebBrowser.cs | 126 ++++++++++++++++++++++ OpenTween/OpenTween.csproj | 2 + 5 files changed, 206 insertions(+), 61 deletions(-) create mode 100644 OpenTween/Models/ColorRGB.cs create mode 100644 OpenTween/Models/MediaViewerWebBrowser.cs diff --git a/OpenTween/MediaHandler.cs b/OpenTween/MediaHandler.cs index a6af1e02d..1a2efdb6d 100644 --- a/OpenTween/MediaHandler.cs +++ b/OpenTween/MediaHandler.cs @@ -89,9 +89,10 @@ public async Task OpenMediaInLightViewer(IWin32Window owner, ThumbnailInfo[] thu public void OpenMediaInWebBrowserViewer(IWin32Window owner, ThumbnailInfo[] thumbnails, int displayIndex) { - using var viewerDialog = new MediaViewerWebBrowserDialog(); + var viewer = new MediaViewerWebBrowser(); + viewer.SetMediaItem(thumbnails[displayIndex]); - viewerDialog.SetMediaItem(thumbnails[displayIndex]); + using var viewerDialog = new MediaViewerWebBrowserDialog(viewer); viewerDialog.ShowDialog(owner); } } diff --git a/OpenTween/MediaViewerWebBrowserDialog.cs b/OpenTween/MediaViewerWebBrowserDialog.cs index ae9318d7e..605524509 100644 --- a/OpenTween/MediaViewerWebBrowserDialog.cs +++ b/OpenTween/MediaViewerWebBrowserDialog.cs @@ -21,78 +21,50 @@ #nullable enable -using System; -using System.Net; -using OpenTween.Thumbnail; +using System.ComponentModel; +using OpenTween.Models; namespace OpenTween { public partial class MediaViewerWebBrowserDialog : OTBaseForm { - public MediaViewerWebBrowserDialog() + private readonly MediaViewerWebBrowser model; + + public MediaViewerWebBrowserDialog(MediaViewerWebBrowser model) { this.InitializeComponent(); - } - public void SetMediaItem(ThumbnailInfo media) - => this.webBrowser.DocumentText = this.CreateDocument(media); + this.model = model; + this.model.SetBackColor(new ColorRGB(this.BackColor)); - internal string CreateDocument(ThumbnailInfo media) - { - const string TEMPLATE_HEAD = @" - - - -MediaViewerWebBrowserDialog - -"; - var bgColor = this.BackColor; - var html = TEMPLATE_HEAD - .Replace("###BG_COLOR###", $"{bgColor.R},{bgColor.G},{bgColor.B}"); + this.model.PropertyChanged += + (s, e) => this.InvokeAsync(() => this.Model_PropertyChanged(s, e)); - if (media.VideoUrl != null) - { - const string TEMPLATE_VIDEO_BODY = @" -
- -
-"; - html += TEMPLATE_VIDEO_BODY - .Replace("###VIDEO_URI###", WebUtility.HtmlEncode(media.VideoUrl)); - } - else + this.UpdateAll(); + } + + private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) { - const string TEMPLATE_IMAGE_BODY = @" -
-
-
-"; - html += TEMPLATE_IMAGE_BODY - .Replace("###IMAGE_URI###", WebUtility.HtmlEncode(Uri.EscapeUriString(media.FullSizeImageUrl ?? media.ThumbnailImageUrl ?? ""))); + case nameof(MediaViewerWebBrowser.DisplayHTML): + this.UpdateHTML(); + break; + case "": + case null: + this.UpdateAll(); + break; + default: + break; } + } - return html; + private void UpdateAll() + { + this.UpdateHTML(); } + + private void UpdateHTML() + => this.webBrowser.DocumentText = this.model.DisplayHTML; } } diff --git a/OpenTween/Models/ColorRGB.cs b/OpenTween/Models/ColorRGB.cs new file mode 100644 index 000000000..687eb3244 --- /dev/null +++ b/OpenTween/Models/ColorRGB.cs @@ -0,0 +1,44 @@ +// OpenTween - Client of Twitter +// Copyright (c) 2018 kim_upsilon (@kim_upsilon) +// All rights reserved. +// +// This file is part of OpenTween. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see , or write to +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +// Boston, MA 02110-1301, USA. + +#nullable enable + +namespace OpenTween.Models +{ + public readonly struct ColorRGB + { + public readonly int R; + public readonly int G; + public readonly int B; + + public ColorRGB(int r, int g, int b) + { + this.R = r; + this.G = g; + this.B = b; + } + + public ColorRGB(System.Drawing.Color color) + : this(color.R, color.G, color.B) + { + } + } +} diff --git a/OpenTween/Models/MediaViewerWebBrowser.cs b/OpenTween/Models/MediaViewerWebBrowser.cs new file mode 100644 index 000000000..7435577ec --- /dev/null +++ b/OpenTween/Models/MediaViewerWebBrowser.cs @@ -0,0 +1,126 @@ +// OpenTween - Client of Twitter +// Copyright (c) 2018 kim_upsilon (@kim_upsilon) +// All rights reserved. +// +// This file is part of OpenTween. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see , or write to +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +// Boston, MA 02110-1301, USA. + +#nullable enable + +using System; +using System.Net; +using OpenTween.Thumbnail; + +namespace OpenTween.Models +{ + public class MediaViewerWebBrowser : NotifyPropertyChangedBase + { + private ThumbnailInfo? displayMedia; + private string displayHTML = ""; + private ColorRGB backColor = new ColorRGB(0, 0, 0); + + public ThumbnailInfo DisplayMedia + { + get => this.displayMedia ?? throw new InvalidOperationException("DispalyMedia is not set"); + private set => this.SetProperty(ref this.displayMedia, value); + } + + public string DisplayHTML + { + get => this.displayHTML; + private set => this.SetProperty(ref this.displayHTML, value); + } + + public ColorRGB BackColor + { + get => this.backColor; + private set => this.SetProperty(ref this.backColor, value); + } + + public void SetMediaItem(ThumbnailInfo media) + { + this.DisplayMedia = media; + this.DisplayHTML = this.CreateDocument(); + } + + public void SetBackColor(ColorRGB color) + { + this.BackColor = color; + this.DisplayHTML = this.CreateDocument(); + } + + private string CreateDocument() + { + const string TEMPLATE_HEAD = @" + + + +MediaViewerWebBrowserDialog + +"; + var bgColor = this.BackColor; + var html = TEMPLATE_HEAD + .Replace("###BG_COLOR###", $"{bgColor.R},{bgColor.G},{bgColor.B}"); + + var media = this.DisplayMedia; + + if (media.VideoUrl != null) + { + const string TEMPLATE_VIDEO_BODY = @" +
+ +
+"; + html += TEMPLATE_VIDEO_BODY + .Replace("###VIDEO_URI###", WebUtility.HtmlEncode(media.VideoUrl)); + } + else + { + const string TEMPLATE_IMAGE_BODY = @" +
+
+
+"; + html += TEMPLATE_IMAGE_BODY + .Replace("###IMAGE_URI###", WebUtility.HtmlEncode(Uri.EscapeUriString(media.FullSizeImageUrl ?? media.ThumbnailImageUrl ?? ""))); + } + + return html; + } + } +} diff --git a/OpenTween/OpenTween.csproj b/OpenTween/OpenTween.csproj index 8958542a5..9140c3e59 100644 --- a/OpenTween/OpenTween.csproj +++ b/OpenTween/OpenTween.csproj @@ -170,6 +170,7 @@ LoginDialog.cs
+ @@ -181,6 +182,7 @@ + From 9cd35c3f3a25f36b338c7214b829acafeb96b590 Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Tue, 1 May 2018 07:17:28 +0900 Subject: [PATCH 10/13] =?UTF-8?q?MediaViewerWebBrowser=E3=81=A7=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E4=B8=AD=E3=81=AE=E7=94=BB=E5=83=8F=E3=81=AE=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=83=87=E3=83=83=E3=82=AF=E3=82=B9=E3=82=92=E6=8C=81?= =?UTF-8?q?=E3=81=9F=E3=81=9B=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTween/MediaHandler.cs | 3 ++- OpenTween/MediaViewerWebBrowserDialog.cs | 18 +++++++++++++++ OpenTween/Models/MediaViewerWebBrowser.cs | 28 ++++++++++++++++++----- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/OpenTween/MediaHandler.cs b/OpenTween/MediaHandler.cs index 1a2efdb6d..de6e5f4ef 100644 --- a/OpenTween/MediaHandler.cs +++ b/OpenTween/MediaHandler.cs @@ -90,7 +90,8 @@ public async Task OpenMediaInLightViewer(IWin32Window owner, ThumbnailInfo[] thu public void OpenMediaInWebBrowserViewer(IWin32Window owner, ThumbnailInfo[] thumbnails, int displayIndex) { var viewer = new MediaViewerWebBrowser(); - viewer.SetMediaItem(thumbnails[displayIndex]); + viewer.SetMediaItems(thumbnails); + viewer.SelectMedia(displayIndex); using var viewerDialog = new MediaViewerWebBrowserDialog(viewer); viewerDialog.ShowDialog(owner); diff --git a/OpenTween/MediaViewerWebBrowserDialog.cs b/OpenTween/MediaViewerWebBrowserDialog.cs index 605524509..7460a2a01 100644 --- a/OpenTween/MediaViewerWebBrowserDialog.cs +++ b/OpenTween/MediaViewerWebBrowserDialog.cs @@ -47,6 +47,10 @@ private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { + case nameof(MediaViewerWebBrowser.MediaItems): + case nameof(MediaViewerWebBrowser.DisplayMediaIndex): + this.UpdateTitle(); + break; case nameof(MediaViewerWebBrowser.DisplayHTML): this.UpdateHTML(); break; @@ -61,9 +65,23 @@ private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e) private void UpdateAll() { + this.UpdateTitle(); this.UpdateHTML(); } + private void UpdateTitle() + { + const string TITLE_TEMPLATE = "{0}/{1}"; + + var mediaCount = this.model.MediaItems.Length; + var displayIndex = this.model.DisplayMediaIndex; + + if (mediaCount == 1) + this.Text = ""; + else + this.Text = string.Format(TITLE_TEMPLATE, displayIndex + 1, mediaCount); + } + private void UpdateHTML() => this.webBrowser.DocumentText = this.model.DisplayHTML; } diff --git a/OpenTween/Models/MediaViewerWebBrowser.cs b/OpenTween/Models/MediaViewerWebBrowser.cs index 7435577ec..b93aef2d0 100644 --- a/OpenTween/Models/MediaViewerWebBrowser.cs +++ b/OpenTween/Models/MediaViewerWebBrowser.cs @@ -29,16 +29,26 @@ namespace OpenTween.Models { public class MediaViewerWebBrowser : NotifyPropertyChangedBase { - private ThumbnailInfo? displayMedia; + private ThumbnailInfo[] mediaItems = Array.Empty(); + private int displayMediaIndex; private string displayHTML = ""; private ColorRGB backColor = new ColorRGB(0, 0, 0); - public ThumbnailInfo DisplayMedia + public ThumbnailInfo[] MediaItems { - get => this.displayMedia ?? throw new InvalidOperationException("DispalyMedia is not set"); - private set => this.SetProperty(ref this.displayMedia, value); + get => this.mediaItems; + private set => this.SetProperty(ref this.mediaItems, value); } + public int DisplayMediaIndex + { + get => this.displayMediaIndex; + private set => this.SetProperty(ref this.displayMediaIndex, value); + } + + public ThumbnailInfo DisplayMedia + => this.MediaItems[this.DisplayMediaIndex]; + public string DisplayHTML { get => this.displayHTML; @@ -51,9 +61,15 @@ public ColorRGB BackColor private set => this.SetProperty(ref this.backColor, value); } - public void SetMediaItem(ThumbnailInfo media) + public void SetMediaItems(ThumbnailInfo[] thumbnails) + { + this.DisplayMediaIndex = 0; + this.MediaItems = thumbnails; + } + + public void SelectMedia(int displayIndex) { - this.DisplayMedia = media; + this.DisplayMediaIndex = displayIndex; this.DisplayHTML = this.CreateDocument(); } From 95ff048f2477333e2a279159614378580302373e Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Tue, 1 May 2018 08:48:00 +0900 Subject: [PATCH 11/13] =?UTF-8?q?MediaViewerWebBrowserDialog=E3=81=AB?= =?UTF-8?q?=E8=BB=BD=E9=87=8F=E3=83=93=E3=83=A5=E3=83=BC=E3=82=A2=E3=83=BC?= =?UTF-8?q?=E7=89=88=E3=81=A8=E5=90=8C=E6=A7=98=E3=81=AE=E3=82=AD=E3=83=BC?= =?UTF-8?q?=E3=83=90=E3=82=A4=E3=83=B3=E3=83=89=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTween/MediaHandler.cs | 1 + .../MediaViewerWebBrowserDialog.Designer.cs | 2 +- OpenTween/MediaViewerWebBrowserDialog.cs | 38 ++++++++++++++++++- OpenTween/Models/MediaViewerWebBrowser.cs | 18 +++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/OpenTween/MediaHandler.cs b/OpenTween/MediaHandler.cs index de6e5f4ef..7db43799f 100644 --- a/OpenTween/MediaHandler.cs +++ b/OpenTween/MediaHandler.cs @@ -94,6 +94,7 @@ public void OpenMediaInWebBrowserViewer(IWin32Window owner, ThumbnailInfo[] thum viewer.SelectMedia(displayIndex); using var viewerDialog = new MediaViewerWebBrowserDialog(viewer); + viewerDialog.OpenInBrowser = this.OpenInBrowser; viewerDialog.ShowDialog(owner); } } diff --git a/OpenTween/MediaViewerWebBrowserDialog.Designer.cs b/OpenTween/MediaViewerWebBrowserDialog.Designer.cs index 90ac0014f..2746b3be4 100644 --- a/OpenTween/MediaViewerWebBrowserDialog.Designer.cs +++ b/OpenTween/MediaViewerWebBrowserDialog.Designer.cs @@ -33,7 +33,6 @@ private void InitializeComponent() // // webBrowser // - this.webBrowser.AllowNavigation = false; this.webBrowser.AllowWebBrowserDrop = false; this.webBrowser.Dock = System.Windows.Forms.DockStyle.Fill; this.webBrowser.IsWebBrowserContextMenuEnabled = false; @@ -45,6 +44,7 @@ private void InitializeComponent() this.webBrowser.Size = new System.Drawing.Size(500, 500); this.webBrowser.TabIndex = 0; this.webBrowser.WebBrowserShortcutsEnabled = false; + this.webBrowser.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.WebBrowser_PreviewKeyDown); // // MediaViewerWebBrowserDialog // diff --git a/OpenTween/MediaViewerWebBrowserDialog.cs b/OpenTween/MediaViewerWebBrowserDialog.cs index 7460a2a01..e396ca526 100644 --- a/OpenTween/MediaViewerWebBrowserDialog.cs +++ b/OpenTween/MediaViewerWebBrowserDialog.cs @@ -21,13 +21,18 @@ #nullable enable +using System; using System.ComponentModel; +using System.Threading.Tasks; +using System.Windows.Forms; using OpenTween.Models; namespace OpenTween { public partial class MediaViewerWebBrowserDialog : OTBaseForm { + public Func? OpenInBrowser; + private readonly MediaViewerWebBrowser model; public MediaViewerWebBrowserDialog(MediaViewerWebBrowser model) @@ -83,6 +88,37 @@ private void UpdateTitle() } private void UpdateHTML() - => this.webBrowser.DocumentText = this.model.DisplayHTML; + { + using (ControlTransaction.Update(this.webBrowser)) + this.webBrowser.DocumentText = this.model.DisplayHTML; + } + + private async void WebBrowser_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) + { + e.IsInputKey = true; + + switch (e.KeyData) + { + case Keys.Up: + case Keys.Left: + this.model.SelectPreviousMedia(); + break; + case Keys.Down: + case Keys.Right: + this.model.SelectNextMedia(); + break; + case Keys.Enter: + this.Close(); + if (this.OpenInBrowser != null) + await this.OpenInBrowser.Invoke(this, this.model.DisplayMedia.MediaPageUrl); + break; + case Keys.Escape: + this.Close(); + break; + default: + e.IsInputKey = false; + break; + } + } } } diff --git a/OpenTween/Models/MediaViewerWebBrowser.cs b/OpenTween/Models/MediaViewerWebBrowser.cs index b93aef2d0..e2b496e38 100644 --- a/OpenTween/Models/MediaViewerWebBrowser.cs +++ b/OpenTween/Models/MediaViewerWebBrowser.cs @@ -73,6 +73,24 @@ public void SelectMedia(int displayIndex) this.DisplayHTML = this.CreateDocument(); } + public void SelectPreviousMedia() + { + var currentIndex = this.DisplayMediaIndex; + if (currentIndex == 0) + return; + + this.SelectMedia(currentIndex - 1); + } + + public void SelectNextMedia() + { + var currentIndex = this.DisplayMediaIndex; + if (currentIndex == this.MediaItems.Length - 1) + return; + + this.SelectMedia(currentIndex + 1); + } + public void SetBackColor(ColorRGB color) { this.BackColor = color; From d989a1cef102b77f7f1be4e093502406118e93a6 Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Sun, 5 Aug 2018 02:06:16 +0900 Subject: [PATCH 12/13] =?UTF-8?q?MediaViewer=E8=AA=AD=E3=81=BF=E8=BE=BC?= =?UTF-8?q?=E3=81=BF=E5=AE=8C=E4=BA=86=E5=BE=8C=E3=81=AB=E7=94=BB=E5=83=8F?= =?UTF-8?q?=E3=81=AB=E3=83=95=E3=82=A9=E3=83=BC=E3=82=AB=E3=82=B9=E3=82=92?= =?UTF-8?q?=E5=BD=93=E3=81=A6=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTween/MediaViewerWebBrowserDialog.Designer.cs | 1 + OpenTween/MediaViewerWebBrowserDialog.cs | 3 +++ OpenTween/Models/MediaViewerWebBrowser.cs | 5 +++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/OpenTween/MediaViewerWebBrowserDialog.Designer.cs b/OpenTween/MediaViewerWebBrowserDialog.Designer.cs index 2746b3be4..2883b2e67 100644 --- a/OpenTween/MediaViewerWebBrowserDialog.Designer.cs +++ b/OpenTween/MediaViewerWebBrowserDialog.Designer.cs @@ -44,6 +44,7 @@ private void InitializeComponent() this.webBrowser.Size = new System.Drawing.Size(500, 500); this.webBrowser.TabIndex = 0; this.webBrowser.WebBrowserShortcutsEnabled = false; + this.webBrowser.DocumentCompleted += new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(this.WebBrowser_DocumentCompleted); this.webBrowser.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.WebBrowser_PreviewKeyDown); // // MediaViewerWebBrowserDialog diff --git a/OpenTween/MediaViewerWebBrowserDialog.cs b/OpenTween/MediaViewerWebBrowserDialog.cs index e396ca526..20ce28485 100644 --- a/OpenTween/MediaViewerWebBrowserDialog.cs +++ b/OpenTween/MediaViewerWebBrowserDialog.cs @@ -120,5 +120,8 @@ private async void WebBrowser_PreviewKeyDown(object sender, PreviewKeyDownEventA break; } } + + private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) + => this.webBrowser.Document.GetElementById("currentMedia")?.Focus(); } } diff --git a/OpenTween/Models/MediaViewerWebBrowser.cs b/OpenTween/Models/MediaViewerWebBrowser.cs index e2b496e38..88e38ea06 100644 --- a/OpenTween/Models/MediaViewerWebBrowser.cs +++ b/OpenTween/Models/MediaViewerWebBrowser.cs @@ -135,7 +135,7 @@ private string CreateDocument() { const string TEMPLATE_VIDEO_BODY = @"
-
@@ -147,7 +147,8 @@ private string CreateDocument() { const string TEMPLATE_IMAGE_BODY = @"
-
+
+
"; html += TEMPLATE_IMAGE_BODY From 727d646094ab422e3be54aa6650b12976a96f426 Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Sun, 5 Aug 2018 02:07:13 +0900 Subject: [PATCH 13/13] =?UTF-8?q?MediaViewer=E3=81=A7=E9=96=B2=E8=A6=A7?= =?UTF-8?q?=E4=B8=AD=E3=81=AE=E7=94=BB=E5=83=8F=E3=81=AB=E3=83=84=E3=83=BC?= =?UTF-8?q?=E3=83=AB=E3=83=81=E3=83=83=E3=83=97=E3=81=A7alt=5Ftext?= =?UTF-8?q?=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTween/Models/MediaViewerWebBrowser.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/OpenTween/Models/MediaViewerWebBrowser.cs b/OpenTween/Models/MediaViewerWebBrowser.cs index 88e38ea06..7b59349ca 100644 --- a/OpenTween/Models/MediaViewerWebBrowser.cs +++ b/OpenTween/Models/MediaViewerWebBrowser.cs @@ -135,24 +135,28 @@ private string CreateDocument() { const string TEMPLATE_VIDEO_BODY = @"
-
"; html += TEMPLATE_VIDEO_BODY - .Replace("###VIDEO_URI###", WebUtility.HtmlEncode(media.VideoUrl)); + .Replace("###VIDEO_URI###", WebUtility.HtmlEncode(media.VideoUrl)) + .Replace("###MEDIA_TOOLTIP###", WebUtility.HtmlEncode(media.TooltipText)); } else { const string TEMPLATE_IMAGE_BODY = @"
-
+
"; html += TEMPLATE_IMAGE_BODY - .Replace("###IMAGE_URI###", WebUtility.HtmlEncode(Uri.EscapeUriString(media.FullSizeImageUrl ?? media.ThumbnailImageUrl ?? ""))); + .Replace("###IMAGE_URI###", WebUtility.HtmlEncode(Uri.EscapeUriString(media.FullSizeImageUrl ?? media.ThumbnailImageUrl ?? ""))) + .Replace("###MEDIA_TOOLTIP###", WebUtility.HtmlEncode(media.TooltipText)); } return html;