/// <summary>キャッシュする範囲の終了インデックス</summary>
public int EndIndex { get; set; }
- /// <summary>キャッシュされた <see cref="ListViewItem"/> インスタンス</summary>
- public ListViewItem[] ListItem { get; set; }
-
- /// <summary>キャッシュされた範囲に対応する <see cref="PostClass"/> インスタンス</summary>
- public PostClass[] Post { get; set; }
+ /// <summary>キャッシュされた範囲に対応する <see cref="ListViewItem"/> と <see cref="PostClass"/> の組</summary>
+ public (ListViewItem, PostClass)[] Cache { get; set; }
/// <summary>キャッシュされたアイテムの件数</summary>
public int Count
{
if (this.Contains(index))
{
- item = this.ListItem[index - this.StartIndex];
- post = this.Post[index - this.StartIndex];
+ (item, post) = this.Cache[index - this.StartIndex];
return true;
}
else
private string[] ColumnText = new string[9];
private bool _DoFavRetweetFlags = false;
- private bool osResumed = false;
//////////////////////////////////////////////////////////////////////////////////////////////////////////
- private System.Timers.Timer TimerTimeline = new System.Timers.Timer();
+ private readonly TimelineScheduler timelineScheduler = new TimelineScheduler();
private ThrottlingTimer RefreshThrottlingTimer;
+ private ThrottlingTimer colorizeDebouncer;
private ThrottlingTimer selectionDebouncer;
private ThrottlingTimer saveConfigDebouncer;
//タイマー設定
+ this.timelineScheduler.UpdateHome = () => this.InvokeAsync(() => this.RefreshTabAsync<HomeTabModel>());
+ this.timelineScheduler.UpdateMention = () => this.InvokeAsync(() => this.RefreshTabAsync<MentionsTabModel>());
+ this.timelineScheduler.UpdateDm = () => this.InvokeAsync(() => this.RefreshTabAsync<DirectMessagesTabModel>());
+ this.timelineScheduler.UpdatePublicSearch = () => this.InvokeAsync(() => this.RefreshTabAsync<PublicSearchTabModel>());
+ this.timelineScheduler.UpdateUser = () => this.InvokeAsync(() => this.RefreshTabAsync<UserTimelineTabModel>());
+ this.timelineScheduler.UpdateList = () => this.InvokeAsync(() => this.RefreshTabAsync<ListTimelineTabModel>());
+ this.timelineScheduler.UpdateConfig = () => this.InvokeAsync(() => Task.WhenAll(new[]
+ {
+ this.doGetFollowersMenu(),
+ this.RefreshBlockIdsAsync(),
+ this.RefreshMuteUserIdsAsync(),
+ this.RefreshNoRetweetIdsAsync(),
+ this.RefreshTwitterConfigurationAsync(),
+ }));
+ this.RefreshTimelineScheduler();
+
var streamingRefreshInterval = TimeSpan.FromSeconds(SettingManager.Common.UserstreamPeriod);
this.RefreshThrottlingTimer = ThrottlingTimer.Throttle(() => this.InvokeAsync(() => this.RefreshTimeline()), streamingRefreshInterval);
+ this.colorizeDebouncer = ThrottlingTimer.Debounce(() => this.InvokeAsync(() => this.ColorizeList()), TimeSpan.FromMilliseconds(100), leading: true);
this.selectionDebouncer = ThrottlingTimer.Debounce(() => this.InvokeAsync(() => this.UpdateSelectedPost()), TimeSpan.FromMilliseconds(100), leading: true);
this.saveConfigDebouncer = ThrottlingTimer.Debounce(() => this.InvokeAsync(() => this.SaveConfigsAll(ifModified: true)), TimeSpan.FromSeconds(1));
- TimerTimeline.AutoReset = true;
- TimerTimeline.SynchronizingObject = this;
- //Recent取得間隔
- TimerTimeline.Interval = 1000;
- TimerTimeline.Enabled = true;
//更新中アイコンアニメーション間隔
TimerRefreshIcon.Interval = 200;
TimerRefreshIcon.Enabled = false;
private void TimerInterval_Changed(object sender, IntervalChangedEventArgs e) //Handles SettingDialog.IntervalChanged
{
- if (!TimerTimeline.Enabled) return;
-
if (e.UserStream)
{
var interval = TimeSpan.FromSeconds(SettingManager.Common.UserstreamPeriod);
oldTimer.Dispose();
}
- ResetTimers = e;
+ this.RefreshTimelineScheduler();
}
- private IntervalChangedEventArgs ResetTimers = IntervalChangedEventArgs.ResetAll;
-
- private static int homeCounter = 0;
- private static int mentionCounter = 0;
- private static int dmCounter = 0;
- private static int pubSearchCounter = 0;
- private static int userTimelineCounter = 0;
- private static int listsCounter = 0;
- private static int ResumeWait = 0;
- private static int refreshFollowers = 0;
-
- private async void TimerTimeline_Elapsed(object sender, EventArgs e)
+ private void RefreshTimelineScheduler()
{
- if (homeCounter > 0) Interlocked.Decrement(ref homeCounter);
- if (mentionCounter > 0) Interlocked.Decrement(ref mentionCounter);
- if (dmCounter > 0) Interlocked.Decrement(ref dmCounter);
- if (pubSearchCounter > 0) Interlocked.Decrement(ref pubSearchCounter);
- if (userTimelineCounter > 0) Interlocked.Decrement(ref userTimelineCounter);
- if (listsCounter > 0) Interlocked.Decrement(ref listsCounter);
- Interlocked.Increment(ref refreshFollowers);
-
- var refreshTasks = new List<Task>();
-
- ////タイマー初期化
- if (ResetTimers.Timeline || homeCounter <= 0 && SettingManager.Common.TimelinePeriod > 0)
- {
- Interlocked.Exchange(ref homeCounter, SettingManager.Common.TimelinePeriod);
- if (!tw.IsUserstreamDataReceived && !ResetTimers.Timeline)
- refreshTasks.Add(this.RefreshTabAsync<HomeTabModel>());
- ResetTimers.Timeline = false;
- }
- if (ResetTimers.Reply || mentionCounter <= 0 && SettingManager.Common.ReplyPeriod > 0)
- {
- Interlocked.Exchange(ref mentionCounter, SettingManager.Common.ReplyPeriod);
- if (!tw.IsUserstreamDataReceived && !ResetTimers.Reply)
- refreshTasks.Add(this.RefreshTabAsync<MentionsTabModel>());
- ResetTimers.Reply = false;
- }
- if (ResetTimers.DirectMessage || dmCounter <= 0 && SettingManager.Common.DMPeriod > 0)
- {
- Interlocked.Exchange(ref dmCounter, SettingManager.Common.DMPeriod);
- if (!tw.IsUserstreamDataReceived && !ResetTimers.DirectMessage)
- refreshTasks.Add(this.RefreshTabAsync<DirectMessagesTabModel>());
- ResetTimers.DirectMessage = false;
- }
- if (ResetTimers.PublicSearch || pubSearchCounter <= 0 && SettingManager.Common.PubSearchPeriod > 0)
- {
- Interlocked.Exchange(ref pubSearchCounter, SettingManager.Common.PubSearchPeriod);
- if (!ResetTimers.PublicSearch)
- refreshTasks.Add(this.RefreshTabAsync<PublicSearchTabModel>());
- ResetTimers.PublicSearch = false;
- }
- if (ResetTimers.UserTimeline || userTimelineCounter <= 0 && SettingManager.Common.UserTimelinePeriod > 0)
- {
- Interlocked.Exchange(ref userTimelineCounter, SettingManager.Common.UserTimelinePeriod);
- if (!ResetTimers.UserTimeline)
- refreshTasks.Add(this.RefreshTabAsync<UserTimelineTabModel>());
- ResetTimers.UserTimeline = false;
- }
- if (ResetTimers.Lists || listsCounter <= 0 && SettingManager.Common.ListsPeriod > 0)
- {
- Interlocked.Exchange(ref listsCounter, SettingManager.Common.ListsPeriod);
- if (!ResetTimers.Lists)
- refreshTasks.Add(this.RefreshTabAsync<ListTimelineTabModel>());
- ResetTimers.Lists = false;
- }
- if (refreshFollowers > 6 * 3600)
- {
- Interlocked.Exchange(ref refreshFollowers, 0);
- refreshTasks.AddRange(new[]
- {
- this.doGetFollowersMenu(),
- this.RefreshNoRetweetIdsAsync(),
- this.RefreshTwitterConfigurationAsync(),
- });
- }
- if (osResumed)
- {
- Interlocked.Increment(ref ResumeWait);
- if (ResumeWait > 30)
- {
- osResumed = false;
- Interlocked.Exchange(ref ResumeWait, 0);
- refreshTasks.AddRange(new[]
- {
- this.RefreshTabAsync<HomeTabModel>(),
- this.RefreshTabAsync<MentionsTabModel>(),
- this.RefreshTabAsync<DirectMessagesTabModel>(),
- this.RefreshTabAsync<PublicSearchTabModel>(),
- this.RefreshTabAsync<UserTimelineTabModel>(),
- this.RefreshTabAsync<ListTimelineTabModel>(),
- this.doGetFollowersMenu(),
- this.RefreshTwitterConfigurationAsync(),
- });
- }
- }
+ this.timelineScheduler.UpdateIntervalHome = TimeSpan.FromSeconds(SettingManager.Common.TimelinePeriod);
+ this.timelineScheduler.UpdateIntervalMention = TimeSpan.FromSeconds(SettingManager.Common.ReplyPeriod);
+ this.timelineScheduler.UpdateIntervalDm = TimeSpan.FromSeconds(SettingManager.Common.DMPeriod);
+ this.timelineScheduler.UpdateIntervalPublicSearch = TimeSpan.FromSeconds(SettingManager.Common.PubSearchPeriod);
+ this.timelineScheduler.UpdateIntervalUser = TimeSpan.FromSeconds(SettingManager.Common.UserTimelinePeriod);
+ this.timelineScheduler.UpdateIntervalList = TimeSpan.FromSeconds(SettingManager.Common.ListsPeriod);
+ this.timelineScheduler.UpdateIntervalConfig = TimeSpan.FromHours(6);
+ this.timelineScheduler.UpdateAfterSystemResume = TimeSpan.FromSeconds(30);
- await Task.WhenAll(refreshTasks);
+ this.timelineScheduler.RefreshSchedule();
}
private void MarkSettingCommonModified()
var post = this.CurrentPost;
this._statuses.SetReadAllTab(post.StatusId, read: true);
+
//キャッシュの書き換え
ChangeCacheStyleRead(true, index); // 既読へ(フォント、文字色)
- ColorizeList();
+ this.colorizeDebouncer.Call();
this.selectionDebouncer.Call();
}
private void ChangeItemStyleRead(bool Read, ListViewItem Item, PostClass Post, DetailsListView DList)
{
Font fnt;
+ string star;
//フォント
if (Read)
{
fnt = _fntReaded;
- Item.SubItems[5].Text = "";
+ star = "";
}
else
{
fnt = _fntUnread;
- Item.SubItems[5].Text = "★";
+ star = "★";
}
+ if (Item.SubItems[5].Text != star)
+ Item.SubItems[5].Text = star;
+
//文字色
Color cl;
if (Post.IsFav)
{
DList.Update();
if (SettingManager.Common.UseUnreadStyle)
- DList.ChangeItemFontAndColor(Item.Index, cl, fnt);
+ DList.ChangeItemFontAndColor(Item, cl, fnt);
else
- DList.ChangeItemForeColor(Item.Index, cl);
+ DList.ChangeItemForeColor(Item, cl);
//if (_itemCache != null) DList.RedrawItems(_itemCacheIndex, _itemCacheIndex + _itemCache.Length - 1, false);
}
}
if (listCache == null)
return;
- var index = listCache.StartIndex;
var listView = (DetailsListView)listCache.TargetList;
- foreach (var cachedPost in listCache.Post)
+ foreach (var (listViewItem, cachedPost) in listCache.Cache)
{
var backColor = this.JudgeColor(_post, cachedPost);
- listView.ChangeItemBackColor(index++, backColor);
+ listView.ChangeItemBackColor(listViewItem, backColor);
}
}
- private void ColorizeList(ListViewItem Item, int Index)
+ private void ColorizeList(ListViewItem Item, PostClass post, int Index)
{
//Index:更新対象のListviewItem.Index。Colorを返す。
//-1は全キャッシュ。Colorは返さない(ダミーを戻す)
else
_post = this.CurrentPost;
- PostClass tPost = GetCurTabPost(Index);
-
if (_post == null) return;
if (Item.Index == -1)
- Item.BackColor = JudgeColor(_post, tPost);
+ Item.BackColor = JudgeColor(_post, post);
else
- this.CurrentListView.ChangeItemBackColor(Item.Index, JudgeColor(_post, tPost));
+ this.CurrentListView.ChangeItemBackColor(Item, JudgeColor(_post, post));
}
private Color JudgeColor(PostClass BasePost, PostClass TargetPost)
else if (TargetPost.ReplyToList.Any(x => x.UserId == BasePost.UserId))
//その人への返信
cl = _clAtTarget;
- else if (TargetPost.ScreenName.Equals(BasePost.ScreenName, StringComparison.OrdinalIgnoreCase))
+ else if (TargetPost.UserId == BasePost.UserId)
//発言者
cl = _clTarget;
else
_hookGlobalHotkey.UnregisterAllOriginalHotkey();
_ignoreConfigSave = true;
MyCommon._endingFlag = true;
- TimerTimeline.Enabled = false;
+ this.timelineScheduler.Enabled = false;
TimerRefreshIcon.Enabled = false;
}
}
TargetList = this.CurrentListView,
StartIndex = startIndex,
EndIndex = endIndex,
- Post = posts,
- ListItem = listItems,
+ Cache = Enumerable.Zip(listItems, posts, (x, y) => (x, y)).ToArray(),
};
Interlocked.Exchange(ref this._listItemCache, listCache);
ChangeItemStyleRead(read, itm, Post, null);
if (tab.TabName == this.CurrentTabName)
- this.ColorizeList(itm, Index);
+ this.ColorizeList(itm, Post, Index);
return itm;
}
_initial = false;
- TimerTimeline.Enabled = true;
+ this.timelineScheduler.Enabled = true;
}
private async Task doGetFollowersMenu()
this.tweetDetailsView.Owner = this;
- this.TimerTimeline.Elapsed += this.TimerTimeline_Elapsed;
this._hookGlobalHotkey.HotkeyPressed += _hookGlobalHotkey_HotkeyPressed;
this.gh.NotifyClicked += GrowlHelper_Callback;
private void SystemEvents_PowerModeChanged(object sender, Microsoft.Win32.PowerModeChangedEventArgs e)
{
- if (e.Mode == Microsoft.Win32.PowerModes.Resume) osResumed = true;
+ if (e.Mode == Microsoft.Win32.PowerModes.Resume)
+ this.timelineScheduler.SystemResumed();
}
private void SystemEvents_TimeChanged(object sender, EventArgs e)
{
tw.StopUserStream();
}
- TimerTimeline.Enabled = isEnable;
+ this.timelineScheduler.Enabled = isEnable;
}
private void StopRefreshAllMenuItem_CheckedChanged(object sender, EventArgs e)