/// <summary> /// When filtering options change, we refilter the list and change Messages /// to be a new list of those items. /// </summary> private void StartAsyncRefilter(RefilterResultEvent evt) { Task t = new Task(delegate() { var items = from item in m_allItems where AllowItem(item) select item; var messages = new ObservableCollection <ListItem>(items); for (int i = 0; i < messages.Count; ++i) { messages[i].Index = i; } evt.Result = messages; }); t.Start(); }
/// <summary> /// When filtering options change, we refilter the list and change Messages /// to be a new list of those items. /// </summary> private void StartAsyncRefilter(RefilterResultEvent evt) { Task t = new Task(delegate() { var items = from item in m_allItems where AllowItem(item) select item; var messages = new ObservableCollection<ListItem>(items); for (int i = 0; i < messages.Count; ++i) messages[i].Index = i; evt.Result = messages; }); t.Start(); }
/// <summary> /// This is the primary update loop for the UI. Every 250 milliseconds we scour /// each concurrent queue looking for new messages and update the UI with it. /// This may seem like overkill but it ensures that all events are handled in a /// correct/sane ordering. /// </summary> private void dispatcherTimer_Tick(object sender, EventArgs e) { // If IrcDotNet hit an error, reset the connection entirely (by creating a new // TwitchClient and throwing away the old one). if (m_error) { if (m_twitch.IsStreamLive) { ResetConnection(); } } // If we are refiltering (asynchronously) the list, actually stop all processing // on the list until it's complete. This prevents us from modifying the message // list while we are reading from it on another thread. if (m_refilterAsync != null) { if (!m_refilterAsync.Complete) { return; } Messages = m_refilterAsync.Result; ScrollBar.ScrollToEnd(); m_refilterAsync = null; } // Now go through the queue, add messages we find, and process refilter requests. if (m_eventQueue.Count == 0) { return; } bool refilterInPlace = false; Event evt; while (m_eventQueue.TryDequeue(out evt)) { switch (evt.Type) { case EventType.NewListItem: AddItem(((NewListItemEvent)evt).Item); break; case EventType.NewSubscriber: NewSubscriberEvent newSub = (NewSubscriberEvent)evt; m_subs.Add(newSub.User); AddItem(newSub.Item); break; case EventType.NotifySubscriber: m_subs.Add(((NotifySubscriberEvent)evt).User); break; case EventType.RefilterInPlace: refilterInPlace = true; break; case EventType.StatusUpdate: var status = ((StatusEvent)evt).Status; AddItem(ListItem.CreateFromStatus(status)); break; case EventType.DuplicateListItem: var dupe = ((DuplicateListItemEvent)evt).Item; int i = dupe.Index; Debug.Assert(dupe == Messages[i]); Messages.RemoveAt(i); for (; i < Messages.Count; ++i) { Messages[i].Index = i; } AddItem(dupe); break; // If we need to refilter the list (due to the user changing filtering options), // we do that work on a background thread. During that time, we stop all message // processing, hence the return instead of break here. case EventType.RefilterAsyncEvent: m_refilterAsync = ((RefilterResultEvent)evt); StartAsyncRefilter(m_refilterAsync); return; } } // If we were asked to refilter the list in place (that is, remove messages), // then we do that only once, now at the end. if (refilterInPlace) { int i = 0; while (i < Messages.Count) { var curr = Messages[i]; if (!AllowItem(curr)) { Messages.RemoveAt(i); } else { curr.Index = i; i++; } } } // If we have new notifications of subscribers, we need to go back and mark the // questions they asked with a sub icon. if (m_subs.Count > 0) { var needsUpdate = from item in m_allItems where (item.Type == ListItemType.ImportantQuestion || item.Type == ListItemType.Question) && item.SubscriberIcon != System.Windows.Visibility.Visible && m_subs.Contains(item.User) select item; foreach (var item in needsUpdate) { item.SubscriberIcon = System.Windows.Visibility.Visible; } m_subs.Clear(); } }
/// <summary> /// This is the primary update loop for the UI. Every 250 milliseconds we scour /// each concurrent queue looking for new messages and update the UI with it. /// This may seem like overkill but it ensures that all events are handled in a /// correct/sane ordering. /// </summary> private void dispatcherTimer_Tick(object sender, EventArgs e) { // If IrcDotNet hit an error, reset the connection entirely (by creating a new // TwitchClient and throwing away the old one). if (m_error) { if (m_twitch.IsStreamLive) ResetConnection(); } // If we are refiltering (asynchronously) the list, actually stop all processing // on the list until it's complete. This prevents us from modifying the message // list while we are reading from it on another thread. if (m_refilterAsync != null) { if (!m_refilterAsync.Complete) return; Messages = m_refilterAsync.Result; ScrollBar.ScrollToEnd(); m_refilterAsync = null; } // Now go through the queue, add messages we find, and process refilter requests. if (m_eventQueue.Count == 0) return; bool refilterInPlace = false; Event evt; while (m_eventQueue.TryDequeue(out evt)) { switch (evt.Type) { case EventType.NewListItem: AddItem(((NewListItemEvent)evt).Item); break; case EventType.NewSubscriber: NewSubscriberEvent newSub = (NewSubscriberEvent)evt; m_subs.Add(newSub.User); AddItem(newSub.Item); break; case EventType.NotifySubscriber: m_subs.Add(((NotifySubscriberEvent)evt).User); break; case EventType.RefilterInPlace: refilterInPlace = true; break; case EventType.StatusUpdate: var status = ((StatusEvent)evt).Status; AddItem(ListItem.CreateFromStatus(status)); break; case EventType.DuplicateListItem: var dupe = ((DuplicateListItemEvent)evt).Item; int i = dupe.Index; Debug.Assert(dupe == Messages[i]); Messages.RemoveAt(i); for (; i < Messages.Count; ++i) Messages[i].Index = i; AddItem(dupe); break; // If we need to refilter the list (due to the user changing filtering options), // we do that work on a background thread. During that time, we stop all message // processing, hence the return instead of break here. case EventType.RefilterAsyncEvent: m_refilterAsync = ((RefilterResultEvent)evt); StartAsyncRefilter(m_refilterAsync); return; } } // If we were asked to refilter the list in place (that is, remove messages), // then we do that only once, now at the end. if (refilterInPlace) { int i = 0; while (i < Messages.Count) { var curr = Messages[i]; if (!AllowItem(curr)) { Messages.RemoveAt(i); } else { curr.Index = i; i++; } } } // If we have new notifications of subscribers, we need to go back and mark the // questions they asked with a sub icon. if (m_subs.Count > 0) { var needsUpdate = from item in m_allItems where (item.Type == ListItemType.ImportantQuestion || item.Type == ListItemType.Question) && item.SubscriberIcon != System.Windows.Visibility.Visible && m_subs.Contains(item.User) select item; foreach (var item in needsUpdate) item.SubscriberIcon = System.Windows.Visibility.Visible; m_subs.Clear(); } }