1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-09-14 10:32:10 +02:00

Compare commits

..

54 Commits

Author SHA1 Message Date
ea53ce361f Bump edit-design plugin version 2017-10-12 10:58:12 +02:00
2fce80b347 Fix custom font size in edit-design & time font in example notification 2017-10-11 20:42:42 +02:00
373c0b1cc3 Fix separator before 'Open dev tools' in example notification context menu 2017-10-11 20:34:50 +02:00
e5e1b7e608 Fix TweetDeck being unable to detect OS name 2017-10-11 20:33:39 +02:00
7e9221c9e0 Merge branch 'master' of https://github.com/chylex/TweetDuck 2017-10-11 20:24:09 +02:00
6b849f854e Rewrite font size & theme handling, add <html> attributes to notifications 2017-10-11 20:22:32 +02:00
831f6bc744 Add support section to readme 2017-10-10 12:25:52 +02:00
d282a7a537 Fix border-radius in media upload previews 2017-10-08 05:43:54 +02:00
fb2f1e3031 Release 1.10.1 2017-10-08 05:27:38 +02:00
00a0da3df3 Fix detail view screenshots & tweak screenshot timings for iframes separately 2017-10-08 01:42:53 +02:00
8c447b1ffb Maybe fix dragged tweet links not being recognized from certain drag sources 2017-09-29 18:04:44 +02:00
a4841175e8 Fix more border-radius, "Ready to Tweet" alignment, and docked reply close icon position 2017-09-29 16:52:39 +02:00
9b139132a1 Disable reply middle-click in temporary columns & fix random reloads from middle-clicks 2017-09-29 14:58:58 +02:00
4a404ecabc Fix weird button styles in inline replies in modal dialogs 2017-09-29 12:48:00 +02:00
aee758b559 Update reply-account docs & fix error with temporary columns and advanced selector 2017-09-29 12:47:13 +02:00
be060d0386 Add new info about emoji usage to the guide 2017-09-28 17:29:34 +02:00
0195378c10 Release 1.10 2017-09-28 17:09:46 +02:00
bc804c6a53 Allow linking to items in the guide 2017-09-28 13:33:54 +02:00
76b15f1971 Fix wrong emoji names & issues with emoji keyboard on while using :emoji_name: 2017-09-28 01:00:03 +02:00
c4d43c9d5b Fix bugs in :emoji_name: update (character count & duplicate keyboard modal) 2017-09-27 23:56:12 +02:00
e8d3e530de Update guide with a screenshot of the main menu 2017-09-27 17:08:51 +02:00
e145adec58 Bump emoji keyboard plugin version 2017-09-27 14:55:08 +02:00
e2dad3e477 Automatically convert :emoji_name: in emoji keyboard plugin
Closes #160
2017-09-27 13:25:05 +02:00
27bdbde171 Optimize images and scripts in the guide 2017-09-25 21:39:04 +02:00
e9ec27169c Improve guide for small width screens and screen readers 2017-09-25 21:12:47 +02:00
2e24cb634c Add favicon and meta tags to the guide 2017-09-25 19:47:00 +02:00
beb9046055 Add a button to send feedback / bug report to Feedback tab in Options 2017-09-25 17:23:16 +02:00
e57301952c Rewrite introduction text and update styles 2017-09-25 15:59:19 +02:00
7411279e48 Update guide URL 2017-09-25 14:22:19 +02:00
16acfa85b5 Bump project and plugin versions 2017-09-25 14:21:51 +02:00
41ef37f3f0 Fix GIFs in detail view not having the pointer cursor 2017-09-24 23:41:39 +02:00
00d8538726 Fix middle-clicking GIFs not opening the tweet externally
Closes #169
2017-09-24 23:35:31 +02:00
6eeb3f9895 Fix error when hovering a quoted tweet with a video 2017-09-24 23:22:20 +02:00
d19dca6ea5 Add a (currently unimplemented) anonymous data collection option 2017-09-24 15:31:44 +02:00
2008ccdaa4 Add an introduction modal & guide 2017-09-23 23:33:06 +02:00
ba2e62de3a Remove a warning in the reply-account plugin's default config 2017-09-23 10:17:55 +02:00
2b62eb254d Fix quoted tweet and media border radius in notifications & screenshots 2017-09-21 13:39:51 +02:00
31f72b7957 Move browser CSS to a file & optimize CSS injection 2017-09-21 13:19:21 +02:00
fdc4616875 Minify CSS files after build 2017-09-21 10:50:13 +02:00
b7de261d25 Move notification CSS to a file, refactor FontSizeLevel & TweetDeckBridge props 2017-09-21 10:34:57 +02:00
ae78a5a026 Fix screenshots of reply threads 2017-09-21 09:50:48 +02:00
fd2cf5d4d7 Make memory cleanup detect recent activity and fix modals 2017-09-21 09:06:55 +02:00
9f0997be1a Refactor some JS 2017-09-21 08:51:41 +02:00
dbade7f854 Fix crash when clicking video overlay after a playback error 2017-09-17 19:06:12 +02:00
3cdc1e190a Fix configuration.default.js files being minified on build 2017-09-14 19:40:41 +02:00
36bede7211 Tweak build instructions (wording and formatting) 2017-09-12 23:06:40 +02:00
46689bb700 Rewrite build and setup instructions 2017-09-12 22:51:59 +02:00
13e1a6543c Release 1.9.2 2017-09-12 20:29:40 +02:00
820ce9e845 Fix uploading files with uppercase extensions 2017-09-11 18:27:39 +02:00
f17806f4e8 Allow dragging tweet links over columns to open them in detail view
Closes #167
2017-09-10 23:03:03 +02:00
3f5ffc9e10 Fix t.co bypass when middle-clicking links in tweets 2017-09-09 19:16:08 +02:00
aeb0842ab4 Move system tray options to a separate tab 2017-09-08 11:36:34 +02:00
38837ae84c Add option to open search columns before the first column 2017-09-07 15:22:14 +02:00
a4eb6935af Fix 'Theme color tweaks' changing color of some scrollbars it shouldn't change 2017-09-06 12:57:43 +02:00
63 changed files with 2086 additions and 380 deletions

View File

@@ -44,11 +44,15 @@ namespace TweetDuck.Configuration{
// CONFIGURATION DATA // CONFIGURATION DATA
public bool FirstRun { get; set; } = true;
public bool AllowDataCollection { get; set; } = false;
public WindowState BrowserWindow { get; set; } = new WindowState(); public WindowState BrowserWindow { get; set; } = new WindowState();
public WindowState PluginsWindow { get; set; } = new WindowState(); public WindowState PluginsWindow { get; set; } = new WindowState();
public bool ExpandLinksOnHover { get; set; } = true; public bool ExpandLinksOnHover { get; set; } = true;
public bool SwitchAccountSelectors { get; set; } = true; public bool SwitchAccountSelectors { get; set; } = true;
public bool OpenSearchInFirstColumn { get; set; } = true;
public bool BestImageQuality { get; set; } = true; public bool BestImageQuality { get; set; } = true;
public bool EnableSpellCheck { get; set; } = false; public bool EnableSpellCheck { get; set; } = false;
public int VideoPlayerVolume { get; set; } = 50; public int VideoPlayerVolume { get; set; } = 50;

View File

@@ -17,6 +17,7 @@ namespace TweetDuck.Core.Bridge{
if (environment == Environment.Browser){ if (environment == Environment.Browser){
build.Append("x.switchAccountSelectors=").Append(Bool(Program.UserConfig.SwitchAccountSelectors)); build.Append("x.switchAccountSelectors=").Append(Bool(Program.UserConfig.SwitchAccountSelectors));
build.Append("x.openSearchInFirstColumn=").Append(Bool(Program.UserConfig.OpenSearchInFirstColumn));
build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications)); build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications));
build.Append("x.hasCustomNotificationSound=").Append(Bool(Program.UserConfig.NotificationSoundPath.Length > 0)); build.Append("x.hasCustomNotificationSound=").Append(Bool(Program.UserConfig.NotificationSoundPath.Length > 0));
build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews)); build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews));

View File

@@ -11,6 +11,9 @@ using TweetDuck.Resources;
namespace TweetDuck.Core.Bridge{ namespace TweetDuck.Core.Bridge{
sealed class TweetDeckBridge{ sealed class TweetDeckBridge{
public static string FontSize { get; private set; }
public static string NotificationHeadLayout { get; private set; }
public static string LastHighlightedTweetUrl = string.Empty; public static string LastHighlightedTweetUrl = string.Empty;
public static string LastHighlightedQuoteUrl = string.Empty; public static string LastHighlightedQuoteUrl = string.Empty;
private static string LastHighlightedTweetAuthors = string.Empty; private static string LastHighlightedTweetAuthors = string.Empty;
@@ -22,6 +25,7 @@ namespace TweetDuck.Core.Bridge{
private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2); private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
public static void ResetStaticProperties(){ public static void ResetStaticProperties(){
FontSize = NotificationHeadLayout = null;
LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty; LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty;
} }
@@ -46,15 +50,16 @@ namespace TweetDuck.Core.Bridge{
this.notification = notification; this.notification = notification;
} }
public void LoadFontSizeClass(string fsClass){ public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){
form.InvokeAsyncSafe(() => { form.InvokeAsyncSafe(() => {
TweetNotification.SetFontSizeClass(fsClass); form.OnIntroductionClosed(showGuide, allowDataCollection);
}); });
} }
public void LoadNotificationHeadContents(string headContents){ public void LoadNotificationLayout(string fontSize, string headLayout){
form.InvokeAsyncSafe(() => { form.InvokeAsyncSafe(() => {
TweetNotification.SetHeadTag(headContents); FontSize = fontSize;
NotificationHeadLayout = headLayout;
}); });
} }

View File

@@ -85,6 +85,7 @@ namespace TweetDuck.Core{
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){ this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
DialogHandler = new FileDialogHandler(), DialogHandler = new FileDialogHandler(),
DragHandler = new DragHandlerBrowser(),
MenuHandler = new ContextMenuBrowser(this), MenuHandler = new ContextMenuBrowser(this),
JsDialogHandler = new JavaScriptDialogHandler(), JsDialogHandler = new JavaScriptDialogHandler(),
KeyboardHandler = new KeyboardHandlerBrowser(this), KeyboardHandler = new KeyboardHandlerBrowser(this),
@@ -201,6 +202,7 @@ namespace TweetDuck.Core{
UpdateProperties(PropertyBridge.Environment.Browser); UpdateProperties(PropertyBridge.Environment.Browser);
TweetDeckBridge.RestoreSessionData(e.Frame); TweetDeckBridge.RestoreSessionData(e.Frame);
ScriptLoader.ExecuteFile(e.Frame, "code.js"); ScriptLoader.ExecuteFile(e.Frame, "code.js");
InjectBrowserCSS();
ReinjectCustomCSS(Config.CustomBrowserCSS); ReinjectCustomCSS(Config.CustomBrowserCSS);
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser); plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
@@ -209,6 +211,10 @@ namespace TweetDuck.Core{
if (Program.SystemConfig.EnableBrowserGCReload){ if (Program.SystemConfig.EnableBrowserGCReload){
memoryUsageTracker.Start(this, e.Browser, Program.SystemConfig.BrowserMemoryThreshold); memoryUsageTracker.Start(this, e.Browser, Program.SystemConfig.BrowserMemoryThreshold);
} }
if (Config.FirstRun){
ScriptLoader.ExecuteFile(e.Frame, "introduction.js");
}
} }
} }
@@ -424,6 +430,10 @@ namespace TweetDuck.Core{
// javascript calls // javascript calls
public void InjectBrowserCSS(){
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css").TrimEnd());
}
public void ReinjectCustomCSS(string css){ public void ReinjectCustomCSS(string css){
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty); browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
} }
@@ -438,6 +448,18 @@ namespace TweetDuck.Core{
// callback handlers // callback handlers
public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){
if (Config.FirstRun){
Config.FirstRun = false;
Config.AllowDataCollection = allowDataCollection;
Config.Save();
}
if (showGuide){
ShowChildForm(new FormGuide());
}
}
public void OpenContextMenu(){ public void OpenContextMenu(){
contextMenu.Show(this, PointToClient(Cursor.Position)); contextMenu.Show(this, PointToClient(Cursor.Position));
} }

View File

@@ -20,7 +20,7 @@ namespace TweetDuck.Core{
public static void CloseAllDialogs(){ public static void CloseAllDialogs(){
foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){ foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){
if (form is FormSettings || form is FormPlugins || form is FormAbout){ if (form is FormSettings || form is FormPlugins || form is FormAbout || form is FormGuide){
form.Close(); form.Close();
} }
} }

View File

@@ -0,0 +1,15 @@
using CefSharp;
namespace TweetDuck.Core.Handling{
sealed class ContextMenuGuide : ContextMenuBase{
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
model.Clear();
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
if (HasDevTools){
AddSeparator(model);
AddDebugMenuItems(model);
}
}
}
}

View File

@@ -48,7 +48,7 @@ namespace TweetDuck.Core.Handling{
} }
if (HasDevTools){ if (HasDevTools){
model.AddSeparator(); AddSeparator(model);
AddDebugMenuItems(model); AddDebugMenuItems(model);
} }

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
using CefSharp;
namespace TweetDuck.Core.Handling{
sealed class DragHandlerBrowser : IDragHandler{
public bool OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask){
void TriggerDragStart(string type, string data = null){
browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", type, data);
}
if (dragData.IsLink){
TriggerDragStart("link", dragData.LinkUrl);
}
else if (dragData.IsFragment){
TriggerDragStart("text", dragData.FragmentText.Trim());
}
else{
TriggerDragStart("unknown");
}
return false;
}
public void OnDraggableRegionsChanged(IWebBrowser browserControl, IBrowser browser, IList<DraggableRegion> regions){}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
@@ -20,7 +21,8 @@ namespace TweetDuck.Core.Handling.General{
Filter = $"All Supported Formats ({allFilters})|{allFilters}|All Files (*.*)|*.*" Filter = $"All Supported Formats ({allFilters})|{allFilters}|All Files (*.*)|*.*"
}){ }){
if (dialog.ShowDialog() == DialogResult.OK){ if (dialog.ShowDialog() == DialogResult.OK){
callback.Continue(acceptFilters.FindIndex(filter => filter == Path.GetExtension(dialog.FileName)), dialog.FileNames.ToList()); string ext = Path.GetExtension(dialog.FileName);
callback.Continue(acceptFilters.FindIndex(filter => filter.Equals(ext, StringComparison.OrdinalIgnoreCase)), dialog.FileNames.ToList());
} }
else{ else{
callback.Cancel(); callback.Cancel();

View File

@@ -4,6 +4,7 @@ using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
@@ -12,6 +13,18 @@ using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
partial class FormNotificationBase : Form{ partial class FormNotificationBase : Form{
protected static int FontSizeLevel{
get{
switch(TweetDeckBridge.FontSize){
case "largest": return 4;
case "large": return 3;
case "small": return 1;
case "smallest": return 0;
default: return 2;
}
}
}
protected Point PrimaryLocation{ protected Point PrimaryLocation{
get{ get{
UserConfig config = Program.UserConfig; UserConfig config = Program.UserConfig;

View File

@@ -33,7 +33,7 @@ namespace TweetDuck.Core.Notification{
public bool RequiresResize{ public bool RequiresResize{
get{ get{
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != TweetNotification.FontSizeLevel || CanResizeWindow; return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != FontSizeLevel || CanResizeWindow;
} }
set{ set{
@@ -43,7 +43,7 @@ namespace TweetDuck.Core.Notification{
} }
else{ else{
prevDisplayTimer = Program.UserConfig.DisplayNotificationTimer; prevDisplayTimer = Program.UserConfig.DisplayNotificationTimer;
prevFontSize = TweetNotification.FontSizeLevel; prevFontSize = FontSizeLevel;
} }
} }
} }
@@ -52,7 +52,7 @@ namespace TweetDuck.Core.Notification{
get{ get{
switch(Program.UserConfig.NotificationSize){ switch(Program.UserConfig.NotificationSize){
default: default:
return BrowserUtils.Scale(284, SizeScale*(1.0+0.05*TweetNotification.FontSizeLevel)); return BrowserUtils.Scale(284, SizeScale*(1.0+0.05*FontSizeLevel));
case TweetNotification.Size.Custom: case TweetNotification.Size.Custom:
return Program.UserConfig.CustomNotificationSize.Width; return Program.UserConfig.CustomNotificationSize.Width;
@@ -64,7 +64,7 @@ namespace TweetDuck.Core.Notification{
get{ get{
switch(Program.UserConfig.NotificationSize){ switch(Program.UserConfig.NotificationSize){
default: default:
return BrowserUtils.Scale(122, SizeScale*(1.0+0.075*TweetNotification.FontSizeLevel)); return BrowserUtils.Scale(122, SizeScale*(1.0+0.075*FontSizeLevel));
case TweetNotification.Size.Custom: case TweetNotification.Size.Custom:
return Program.UserConfig.CustomNotificationSize.Height; return Program.UserConfig.CustomNotificationSize.Height;

View File

@@ -22,7 +22,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
browser.LoadingStateChanged += (sender, args) => { browser.LoadingStateChanged += (sender, args) => {
if (!args.IsLoading){ if (!args.IsLoading){
using(IFrame frame = args.Browser.MainFrame){ using(IFrame frame = args.Browser.MainFrame){
ScriptLoader.ExecuteScript(frame, "window.setTimeout($TD_NotificationScreenshot.trigger, 129)", "gen:screenshot"); ScriptLoader.ExecuteScript(frame, "window.setTimeout($TD_NotificationScreenshot.trigger, document.getElementsByTagName('iframe').length ? 267 : 67)", "gen:screenshot");
} }
} }
}; };

View File

@@ -1,27 +1,12 @@
using System; using System;
using System.Text; using System.Text;
using TweetDuck.Core.Bridge;
using TweetDuck.Resources; using TweetDuck.Resources;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
sealed class TweetNotification{ sealed class TweetNotification{
private static string FontSizeClass { get; set; } private const string DefaultHeadLayout = @"<html class='os-windows txt-size--14' data-td-font='medium' data-td-theme='dark'><head><meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>";
private static string HeadTag { get; set; } private static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css");
private const string DefaultFontSizeClass = "medium";
private const string DefaultHeadTag = @"<meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>";
private const string CustomCSS = @"body:before{content:none}body{overflow-y:auto}.scroll-styled-v::-webkit-scrollbar{width:7px}.scroll-styled-v::-webkit-scrollbar-thumb{border-radius:0}.scroll-styled-v::-webkit-scrollbar-track{border-left:0}#td-skip{opacity:0;cursor:pointer;transition:opacity 0.15s ease}.td-hover #td-skip{opacity:0.75}#td-skip:hover{opacity:1}.media-size-medium{height:calc(100vh - 16px)!important;max-height:240px;border-radius:1px!important}.js-quote-detail .media-size-medium{height:calc(100vh - 28px)!important;}.js-media.margin-vm, .js-media-preview-container.margin-vm{margin-bottom:0!important}";
public static int FontSizeLevel{
get{
switch(FontSizeClass){
case "largest": return 4;
case "large": return 3;
case "medium": return 2;
case "small": return 1;
default: return 0;
}
}
}
private static string ExampleTweetHTML; private static string ExampleTweetHTML;
@@ -39,14 +24,6 @@ namespace TweetDuck.Core.Notification{
} }
} }
public static void SetFontSizeClass(string newFSClass){
FontSizeClass = newFSClass;
}
public static void SetHeadTag(string headContents){
HeadTag = headContents;
}
public enum Position{ public enum Position{
TopLeft, TopRight, BottomLeft, BottomRight, Custom TopLeft, TopRight, BottomLeft, BottomRight, Custom
} }
@@ -88,8 +65,7 @@ namespace TweetDuck.Core.Notification{
public string GenerateHtml(string bodyClasses = null, bool enableCustomCSS = true){ public string GenerateHtml(string bodyClasses = null, bool enableCustomCSS = true){
StringBuilder build = new StringBuilder(); StringBuilder build = new StringBuilder();
build.Append("<!DOCTYPE html>"); build.Append("<!DOCTYPE html>");
build.Append("<html class='os-windows txt-base-").Append(FontSizeClass ?? DefaultFontSizeClass).Append("'>"); build.Append(TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout);
build.Append("<head>").Append(HeadTag ?? DefaultHeadTag);
if (enableCustomCSS){ if (enableCustomCSS){
build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>"); build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>");

View File

@@ -131,11 +131,14 @@ namespace TweetDuck.Core.Other {
this.Controls.Add(this.labelDescription); this.Controls.Add(this.labelDescription);
this.Controls.Add(this.pictureLogo); this.Controls.Add(this.pictureLogo);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.HelpButton = true;
this.MaximizeBox = false; this.MaximizeBox = false;
this.MinimizeBox = false; this.MinimizeBox = false;
this.Name = "FormAbout"; this.Name = "FormAbout";
this.ShowIcon = false; this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.FormAbout_HelpButtonClicked);
this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.FormAbout_HelpRequested);
((System.ComponentModel.ISupportInitialize)(this.pictureLogo)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.pictureLogo)).EndInit();
this.tablePanelLinks.ResumeLayout(false); this.tablePanelLinks.ResumeLayout(false);
this.tablePanelLinks.PerformLayout(); this.tablePanelLinks.PerformLayout();

View File

@@ -1,4 +1,6 @@
using System.Windows.Forms; using System.ComponentModel;
using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
@@ -21,5 +23,19 @@ namespace TweetDuck.Core.Other{
private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){ private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
BrowserUtils.OpenExternalBrowserUnsafe(e.Link.LinkData as string); BrowserUtils.OpenExternalBrowserUnsafe(e.Link.LinkData as string);
} }
private void FormAbout_HelpRequested(object sender, HelpEventArgs hlpevent){
ShowGuide();
}
private void FormAbout_HelpButtonClicked(object sender, CancelEventArgs e){
e.Cancel = true;
ShowGuide();
}
private void ShowGuide(){
new FormGuide().Show();
Close();
}
} }
} }

44
Core/Other/FormGuide.Designer.cs generated Normal file
View File

@@ -0,0 +1,44 @@
namespace TweetDuck.Core.Other {
partial class FormGuide {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.SuspendLayout();
//
// FormGuide
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(34)))), ((int)(((byte)(34)))), ((int)(((byte)(34)))));
this.ClientSize = new System.Drawing.Size(424, 282);
this.Icon = global::TweetDuck.Properties.Resources.icon;
this.MinimumSize = new System.Drawing.Size(440, 320);
this.Name = "FormGuide";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.ResumeLayout(false);
}
#endregion
}
}

71
Core/Other/FormGuide.cs Normal file
View File

@@ -0,0 +1,71 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Other{
sealed partial class FormGuide : Form{
private const string GuideUrl = "https://tweetduck.chylex.com/guide/v1/";
private readonly ChromiumWebBrowser browser;
public FormGuide(){
InitializeComponent();
Text = Program.BrandName+" Guide";
FormBrowser owner = FormManager.TryFind<FormBrowser>();
if (owner != null){
Size = new Size(owner.Size.Width*3/4, owner.Size.Height*3/4);
VisibleChanged += (sender, args) => this.MoveToCenter(owner);
}
this.browser = new ChromiumWebBrowser(GuideUrl){
MenuHandler = new ContextMenuGuide(),
JsDialogHandler = new JavaScriptDialogHandler(),
LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBrowser()
};
browser.LoadingStateChanged += browser_LoadingStateChanged;
browser.FrameLoadStart += browser_FrameLoadStart;
browser.BrowserSettings.BackgroundColor = (uint)BackColor.ToArgb();
browser.Dock = DockStyle.None;
browser.Location = ControlExtensions.InvisibleLocation;
Controls.Add(browser);
Disposed += (sender, args) => {
Program.UserConfig.ZoomLevelChanged -= Config_ZoomLevelChanged;
browser.Dispose();
};
Program.UserConfig.ZoomLevelChanged += Config_ZoomLevelChanged;
}
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
if (!e.IsLoading){
this.InvokeAsyncSafe(() => {
browser.Location = Point.Empty;
browser.Dock = DockStyle.Fill;
});
browser.LoadingStateChanged -= browser_LoadingStateChanged;
}
}
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
}
private void Config_ZoomLevelChanged(object sender, EventArgs e){
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
}
}
}

View File

@@ -34,8 +34,10 @@ namespace TweetDuck.Core.Other{
this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1; this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1;
AddButton("General", () => new TabSettingsGeneral(updates)); AddButton("General", () => new TabSettingsGeneral(updates));
AddButton("System Tray", () => new TabSettingsTray());
AddButton("Notifications", () => new TabSettingsNotifications(browser.CreateNotificationForm(false))); AddButton("Notifications", () => new TabSettingsNotifications(browser.CreateNotificationForm(false)));
AddButton("Sounds", () => new TabSettingsSounds()); AddButton("Sounds", () => new TabSettingsSounds());
AddButton("Feedback", () => new TabSettingsFeedback());
AddButton("Advanced", () => new TabSettingsAdvanced(browser.ReinjectCustomCSS)); AddButton("Advanced", () => new TabSettingsAdvanced(browser.ReinjectCustomCSS));
SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]); SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]);

View File

@@ -147,7 +147,15 @@ namespace TweetDuck.Core.Other.Management{
} }
private void process_Exited(object sender, EventArgs e){ private void process_Exited(object sender, EventArgs e){
switch(currentProcess.ExitCode){ int exitCode = currentProcess.ExitCode;
currentProcess.Dispose();
currentProcess = null;
currentPipe.Dispose();
currentPipe = null;
switch(exitCode){
case 3: // CODE_LAUNCH_FAIL case 3: // CODE_LAUNCH_FAIL
if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in a browser?", FormMessage.Yes, FormMessage.No)){ if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in a browser?", FormMessage.Yes, FormMessage.No)){
BrowserUtils.OpenExternalBrowser(lastUrl); BrowserUtils.OpenExternalBrowser(lastUrl);
@@ -163,12 +171,6 @@ namespace TweetDuck.Core.Other.Management{
break; break;
} }
currentProcess.Dispose();
currentProcess = null;
currentPipe.Dispose();
currentPipe = null;
owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe); owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe);
} }

View File

@@ -0,0 +1,134 @@
namespace TweetDuck.Core.Other.Settings {
partial class TabSettingsFeedback {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
this.panelFeedback = new System.Windows.Forms.Panel();
this.labelDataCollectionLink = new System.Windows.Forms.LinkLabel();
this.checkDataCollection = new System.Windows.Forms.CheckBox();
this.labelDataCollection = new System.Windows.Forms.Label();
this.labelFeedback = new System.Windows.Forms.Label();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.btnSendFeedback = new System.Windows.Forms.Button();
this.panelFeedback.SuspendLayout();
this.SuspendLayout();
//
// panelFeedback
//
this.panelFeedback.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelFeedback.Controls.Add(this.btnSendFeedback);
this.panelFeedback.Controls.Add(this.labelDataCollectionLink);
this.panelFeedback.Controls.Add(this.checkDataCollection);
this.panelFeedback.Controls.Add(this.labelDataCollection);
this.panelFeedback.Location = new System.Drawing.Point(9, 31);
this.panelFeedback.Name = "panelFeedback";
this.panelFeedback.Size = new System.Drawing.Size(322, 80);
this.panelFeedback.TabIndex = 1;
//
// labelDataCollectionLink
//
this.labelDataCollectionLink.AutoSize = true;
this.labelDataCollectionLink.LinkArea = new System.Windows.Forms.LinkArea(1, 10);
this.labelDataCollectionLink.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline;
this.labelDataCollectionLink.Location = new System.Drawing.Point(141, 60);
this.labelDataCollectionLink.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelDataCollectionLink.Name = "labelDataCollectionLink";
this.labelDataCollectionLink.Size = new System.Drawing.Size(66, 17);
this.labelDataCollectionLink.TabIndex = 3;
this.labelDataCollectionLink.TabStop = true;
this.labelDataCollectionLink.Text = "(learn more)";
this.labelDataCollectionLink.UseCompatibleTextRendering = true;
this.labelDataCollectionLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.labelDataCollectionLink_LinkClicked);
//
// checkDataCollection
//
this.checkDataCollection.AutoSize = true;
this.checkDataCollection.Location = new System.Drawing.Point(6, 59);
this.checkDataCollection.Margin = new System.Windows.Forms.Padding(6, 5, 0, 3);
this.checkDataCollection.Name = "checkDataCollection";
this.checkDataCollection.Size = new System.Drawing.Size(135, 17);
this.checkDataCollection.TabIndex = 2;
this.checkDataCollection.Text = "Send Anonymous Data";
this.checkDataCollection.UseVisualStyleBackColor = true;
//
// labelDataCollection
//
this.labelDataCollection.AutoSize = true;
this.labelDataCollection.Location = new System.Drawing.Point(3, 41);
this.labelDataCollection.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelDataCollection.Name = "labelDataCollection";
this.labelDataCollection.Size = new System.Drawing.Size(79, 13);
this.labelDataCollection.TabIndex = 1;
this.labelDataCollection.Text = "Data Collection";
//
// labelFeedback
//
this.labelFeedback.AutoSize = true;
this.labelFeedback.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelFeedback.Location = new System.Drawing.Point(6, 8);
this.labelFeedback.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
this.labelFeedback.Name = "labelFeedback";
this.labelFeedback.Size = new System.Drawing.Size(80, 20);
this.labelFeedback.TabIndex = 0;
this.labelFeedback.Text = "Feedback";
//
// btnSendFeedback
//
this.btnSendFeedback.AutoSize = true;
this.btnSendFeedback.Location = new System.Drawing.Point(5, 3);
this.btnSendFeedback.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.btnSendFeedback.Name = "btnSendFeedback";
this.btnSendFeedback.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnSendFeedback.Size = new System.Drawing.Size(164, 23);
this.btnSendFeedback.TabIndex = 0;
this.btnSendFeedback.Text = "Send Feedback / Bug Report";
this.btnSendFeedback.UseVisualStyleBackColor = true;
//
// TabSettingsFeedback
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.panelFeedback);
this.Controls.Add(this.labelFeedback);
this.Name = "TabSettingsFeedback";
this.Size = new System.Drawing.Size(340, 122);
this.panelFeedback.ResumeLayout(false);
this.panelFeedback.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Panel panelFeedback;
private System.Windows.Forms.CheckBox checkDataCollection;
private System.Windows.Forms.Label labelDataCollection;
private System.Windows.Forms.Label labelFeedback;
private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.LinkLabel labelDataCollectionLink;
private System.Windows.Forms.Button btnSendFeedback;
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Windows.Forms;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Other.Settings{
sealed partial class TabSettingsFeedback : BaseTabSettings{
public TabSettingsFeedback(){
InitializeComponent();
checkDataCollection.Checked = Config.AllowDataCollection;
}
public override void OnReady(){
btnSendFeedback.Click += btnSendFeedback_Click;
checkDataCollection.CheckedChanged += checkDataCollection_CheckedChanged;
}
private void btnSendFeedback_Click(object sender, EventArgs e){
BrowserUtils.OpenExternalBrowserUnsafe("https://github.com/chylex/TweetDuck/issues/new");
}
private void checkDataCollection_CheckedChanged(object sender, EventArgs e){
Config.AllowDataCollection = checkDataCollection.Checked;
}
private void labelDataCollectionLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
BrowserUtils.OpenExternalBrowserUnsafe("https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data");
}
}
}

View File

@@ -25,29 +25,24 @@
private void InitializeComponent() { private void InitializeComponent() {
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.checkExpandLinks = new System.Windows.Forms.CheckBox(); this.checkExpandLinks = new System.Windows.Forms.CheckBox();
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
this.checkSpellCheck = new System.Windows.Forms.CheckBox(); this.checkSpellCheck = new System.Windows.Forms.CheckBox();
this.checkUpdateNotifications = new System.Windows.Forms.CheckBox(); this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
this.btnCheckUpdates = new System.Windows.Forms.Button(); this.btnCheckUpdates = new System.Windows.Forms.Button();
this.labelZoomValue = new System.Windows.Forms.Label(); this.labelZoomValue = new System.Windows.Forms.Label();
this.checkSwitchAccountSelectors = new System.Windows.Forms.CheckBox(); this.checkSwitchAccountSelectors = new System.Windows.Forms.CheckBox();
this.labelTrayIcon = new System.Windows.Forms.Label(); this.checkBestImageQuality = new System.Windows.Forms.CheckBox();
this.checkOpenSearchInFirstColumn = new System.Windows.Forms.CheckBox();
this.trackBarZoom = new System.Windows.Forms.TrackBar(); this.trackBarZoom = new System.Windows.Forms.TrackBar();
this.labelZoom = new System.Windows.Forms.Label(); this.labelZoom = new System.Windows.Forms.Label();
this.zoomUpdateTimer = new System.Windows.Forms.Timer(this.components); this.zoomUpdateTimer = new System.Windows.Forms.Timer(this.components);
this.labelUI = new System.Windows.Forms.Label(); this.labelUI = new System.Windows.Forms.Label();
this.panelUI = new System.Windows.Forms.Panel(); this.panelUI = new System.Windows.Forms.Panel();
this.labelTray = new System.Windows.Forms.Label();
this.panelUpdates = new System.Windows.Forms.Panel(); this.panelUpdates = new System.Windows.Forms.Panel();
this.panelTray = new System.Windows.Forms.Panel();
this.labelUpdates = new System.Windows.Forms.Label(); this.labelUpdates = new System.Windows.Forms.Label();
this.checkBestImageQuality = new System.Windows.Forms.CheckBox();
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
this.panelUI.SuspendLayout(); this.panelUI.SuspendLayout();
this.panelUpdates.SuspendLayout(); this.panelUpdates.SuspendLayout();
this.panelTray.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// checkExpandLinks // checkExpandLinks
@@ -62,37 +57,14 @@
this.toolTip.SetToolTip(this.checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead."); this.toolTip.SetToolTip(this.checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
this.checkExpandLinks.UseVisualStyleBackColor = true; this.checkExpandLinks.UseVisualStyleBackColor = true;
// //
// comboBoxTrayType
//
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxTrayType.FormattingEnabled = true;
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 5);
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 5, 3, 3);
this.comboBoxTrayType.Name = "comboBoxTrayType";
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 21);
this.comboBoxTrayType.TabIndex = 0;
this.toolTip.SetToolTip(this.comboBoxTrayType, "Changes behavior of the Tray icon.\r\nRight-click the icon for an action menu.");
//
// checkTrayHighlight
//
this.checkTrayHighlight.AutoSize = true;
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 56);
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
this.checkTrayHighlight.Name = "checkTrayHighlight";
this.checkTrayHighlight.Size = new System.Drawing.Size(103, 17);
this.checkTrayHighlight.TabIndex = 2;
this.checkTrayHighlight.Text = "Enable Highlight";
this.toolTip.SetToolTip(this.checkTrayHighlight, "Highlights the tray icon if there are new tweets.\r\nOnly works for columns with popup or audio notifications.\r\nThe icon resets when the main window is restored.");
this.checkTrayHighlight.UseVisualStyleBackColor = true;
//
// checkSpellCheck // checkSpellCheck
// //
this.checkSpellCheck.AutoSize = true; this.checkSpellCheck.AutoSize = true;
this.checkSpellCheck.Location = new System.Drawing.Point(6, 74); this.checkSpellCheck.Location = new System.Drawing.Point(6, 97);
this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkSpellCheck.Name = "checkSpellCheck"; this.checkSpellCheck.Name = "checkSpellCheck";
this.checkSpellCheck.Size = new System.Drawing.Size(119, 17); this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
this.checkSpellCheck.TabIndex = 3; this.checkSpellCheck.TabIndex = 4;
this.checkSpellCheck.Text = "Enable Spell Check"; this.checkSpellCheck.Text = "Enable Spell Check";
this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly."); this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly.");
this.checkSpellCheck.UseVisualStyleBackColor = true; this.checkSpellCheck.UseVisualStyleBackColor = true;
@@ -123,11 +95,11 @@
// labelZoomValue // labelZoomValue
// //
this.labelZoomValue.BackColor = System.Drawing.Color.Transparent; this.labelZoomValue.BackColor = System.Drawing.Color.Transparent;
this.labelZoomValue.Location = new System.Drawing.Point(147, 123); this.labelZoomValue.Location = new System.Drawing.Point(147, 146);
this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelZoomValue.Name = "labelZoomValue"; this.labelZoomValue.Name = "labelZoomValue";
this.labelZoomValue.Size = new System.Drawing.Size(38, 13); this.labelZoomValue.Size = new System.Drawing.Size(38, 13);
this.labelZoomValue.TabIndex = 6; this.labelZoomValue.TabIndex = 7;
this.labelZoomValue.Text = "100%"; this.labelZoomValue.Text = "100%";
this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
this.toolTip.SetToolTip(this.labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots."); this.toolTip.SetToolTip(this.labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots.");
@@ -144,39 +116,53 @@
this.toolTip.SetToolTip(this.checkSwitchAccountSelectors, "When (re)tweeting, click to select a single account or hold Shift to\r\nselect multiple accounts, instead of TweetDeck\'s default behavior."); this.toolTip.SetToolTip(this.checkSwitchAccountSelectors, "When (re)tweeting, click to select a single account or hold Shift to\r\nselect multiple accounts, instead of TweetDeck\'s default behavior.");
this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true; this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true;
// //
// labelTrayIcon // checkBestImageQuality
// //
this.labelTrayIcon.AutoSize = true; this.checkBestImageQuality.AutoSize = true;
this.labelTrayIcon.Location = new System.Drawing.Point(3, 38); this.checkBestImageQuality.Location = new System.Drawing.Point(6, 74);
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0); this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.labelTrayIcon.Name = "labelTrayIcon"; this.checkBestImageQuality.Name = "checkBestImageQuality";
this.labelTrayIcon.Size = new System.Drawing.Size(52, 13); this.checkBestImageQuality.Size = new System.Drawing.Size(114, 17);
this.labelTrayIcon.TabIndex = 1; this.checkBestImageQuality.TabIndex = 3;
this.labelTrayIcon.Text = "Tray Icon"; this.checkBestImageQuality.Text = "Best Image Quality";
this.toolTip.SetToolTip(this.checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
this.checkBestImageQuality.UseVisualStyleBackColor = true;
//
// checkOpenSearchInFirstColumn
//
this.checkOpenSearchInFirstColumn.AutoSize = true;
this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 51);
this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn";
this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(219, 17);
this.checkOpenSearchInFirstColumn.TabIndex = 2;
this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column";
this.toolTip.SetToolTip(this.checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead.");
this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true;
// //
// trackBarZoom // trackBarZoom
// //
this.trackBarZoom.AutoSize = false; this.trackBarZoom.AutoSize = false;
this.trackBarZoom.BackColor = System.Drawing.SystemColors.Control; this.trackBarZoom.BackColor = System.Drawing.SystemColors.Control;
this.trackBarZoom.LargeChange = 25; this.trackBarZoom.LargeChange = 25;
this.trackBarZoom.Location = new System.Drawing.Point(3, 122); this.trackBarZoom.Location = new System.Drawing.Point(3, 145);
this.trackBarZoom.Maximum = 200; this.trackBarZoom.Maximum = 200;
this.trackBarZoom.Minimum = 50; this.trackBarZoom.Minimum = 50;
this.trackBarZoom.Name = "trackBarZoom"; this.trackBarZoom.Name = "trackBarZoom";
this.trackBarZoom.Size = new System.Drawing.Size(148, 30); this.trackBarZoom.Size = new System.Drawing.Size(148, 30);
this.trackBarZoom.SmallChange = 5; this.trackBarZoom.SmallChange = 5;
this.trackBarZoom.TabIndex = 5; this.trackBarZoom.TabIndex = 6;
this.trackBarZoom.TickFrequency = 25; this.trackBarZoom.TickFrequency = 25;
this.trackBarZoom.Value = 100; this.trackBarZoom.Value = 100;
// //
// labelZoom // labelZoom
// //
this.labelZoom.AutoSize = true; this.labelZoom.AutoSize = true;
this.labelZoom.Location = new System.Drawing.Point(3, 106); this.labelZoom.Location = new System.Drawing.Point(3, 129);
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelZoom.Name = "labelZoom"; this.labelZoom.Name = "labelZoom";
this.labelZoom.Size = new System.Drawing.Size(34, 13); this.labelZoom.Size = new System.Drawing.Size(34, 13);
this.labelZoom.TabIndex = 4; this.labelZoom.TabIndex = 5;
this.labelZoom.Text = "Zoom"; this.labelZoom.Text = "Zoom";
// //
// zoomUpdateTimer // zoomUpdateTimer
@@ -199,6 +185,7 @@
// //
this.panelUI.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelUI.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.panelUI.Controls.Add(this.checkOpenSearchInFirstColumn);
this.panelUI.Controls.Add(this.checkBestImageQuality); this.panelUI.Controls.Add(this.checkBestImageQuality);
this.panelUI.Controls.Add(this.checkExpandLinks); this.panelUI.Controls.Add(this.checkExpandLinks);
this.panelUI.Controls.Add(this.checkSwitchAccountSelectors); this.panelUI.Controls.Add(this.checkSwitchAccountSelectors);
@@ -208,85 +195,46 @@
this.panelUI.Controls.Add(this.labelZoomValue); this.panelUI.Controls.Add(this.labelZoomValue);
this.panelUI.Location = new System.Drawing.Point(9, 31); this.panelUI.Location = new System.Drawing.Point(9, 31);
this.panelUI.Name = "panelUI"; this.panelUI.Name = "panelUI";
this.panelUI.Size = new System.Drawing.Size(322, 157); this.panelUI.Size = new System.Drawing.Size(322, 179);
this.panelUI.TabIndex = 1; this.panelUI.TabIndex = 1;
// //
// labelTray
//
this.labelTray.AutoSize = true;
this.labelTray.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelTray.Location = new System.Drawing.Point(6, 212);
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
this.labelTray.Name = "labelTray";
this.labelTray.Size = new System.Drawing.Size(96, 20);
this.labelTray.TabIndex = 2;
this.labelTray.Text = "System Tray";
//
// panelUpdates // panelUpdates
// //
this.panelUpdates.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelUpdates.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.panelUpdates.Controls.Add(this.checkUpdateNotifications); this.panelUpdates.Controls.Add(this.checkUpdateNotifications);
this.panelUpdates.Controls.Add(this.btnCheckUpdates); this.panelUpdates.Controls.Add(this.btnCheckUpdates);
this.panelUpdates.Location = new System.Drawing.Point(9, 358); this.panelUpdates.Location = new System.Drawing.Point(9, 257);
this.panelUpdates.Name = "panelUpdates"; this.panelUpdates.Name = "panelUpdates";
this.panelUpdates.Size = new System.Drawing.Size(322, 55); this.panelUpdates.Size = new System.Drawing.Size(322, 55);
this.panelUpdates.TabIndex = 5; this.panelUpdates.TabIndex = 3;
//
// panelTray
//
this.panelTray.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelTray.Controls.Add(this.checkTrayHighlight);
this.panelTray.Controls.Add(this.comboBoxTrayType);
this.panelTray.Controls.Add(this.labelTrayIcon);
this.panelTray.Location = new System.Drawing.Point(9, 235);
this.panelTray.Name = "panelTray";
this.panelTray.Size = new System.Drawing.Size(322, 76);
this.panelTray.TabIndex = 3;
// //
// labelUpdates // labelUpdates
// //
this.labelUpdates.AutoSize = true; this.labelUpdates.AutoSize = true;
this.labelUpdates.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelUpdates.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelUpdates.Location = new System.Drawing.Point(6, 335); this.labelUpdates.Location = new System.Drawing.Point(6, 234);
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0); this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
this.labelUpdates.Name = "labelUpdates"; this.labelUpdates.Name = "labelUpdates";
this.labelUpdates.Size = new System.Drawing.Size(70, 20); this.labelUpdates.Size = new System.Drawing.Size(70, 20);
this.labelUpdates.TabIndex = 4; this.labelUpdates.TabIndex = 2;
this.labelUpdates.Text = "Updates"; this.labelUpdates.Text = "Updates";
// //
// checkBestImageQuality
//
this.checkBestImageQuality.AutoSize = true;
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 51);
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkBestImageQuality.Name = "checkBestImageQuality";
this.checkBestImageQuality.Size = new System.Drawing.Size(114, 17);
this.checkBestImageQuality.TabIndex = 2;
this.checkBestImageQuality.Text = "Best Image Quality";
this.toolTip.SetToolTip(this.checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
this.checkBestImageQuality.UseVisualStyleBackColor = true;
//
// TabSettingsGeneral // TabSettingsGeneral
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.labelUpdates); this.Controls.Add(this.labelUpdates);
this.Controls.Add(this.panelTray);
this.Controls.Add(this.panelUpdates); this.Controls.Add(this.panelUpdates);
this.Controls.Add(this.labelTray);
this.Controls.Add(this.panelUI); this.Controls.Add(this.panelUI);
this.Controls.Add(this.labelUI); this.Controls.Add(this.labelUI);
this.Name = "TabSettingsGeneral"; this.Name = "TabSettingsGeneral";
this.Size = new System.Drawing.Size(340, 422); this.Size = new System.Drawing.Size(340, 322);
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit();
this.panelUI.ResumeLayout(false); this.panelUI.ResumeLayout(false);
this.panelUI.PerformLayout(); this.panelUI.PerformLayout();
this.panelUpdates.ResumeLayout(false); this.panelUpdates.ResumeLayout(false);
this.panelUpdates.PerformLayout(); this.panelUpdates.PerformLayout();
this.panelTray.ResumeLayout(false);
this.panelTray.PerformLayout();
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout(); this.PerformLayout();
@@ -295,10 +243,7 @@
#endregion #endregion
private System.Windows.Forms.CheckBox checkExpandLinks; private System.Windows.Forms.CheckBox checkExpandLinks;
private System.Windows.Forms.ComboBox comboBoxTrayType;
private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.Label labelTrayIcon;
private System.Windows.Forms.CheckBox checkTrayHighlight;
private System.Windows.Forms.CheckBox checkSpellCheck; private System.Windows.Forms.CheckBox checkSpellCheck;
private System.Windows.Forms.CheckBox checkUpdateNotifications; private System.Windows.Forms.CheckBox checkUpdateNotifications;
private System.Windows.Forms.Button btnCheckUpdates; private System.Windows.Forms.Button btnCheckUpdates;
@@ -309,10 +254,9 @@
private System.Windows.Forms.CheckBox checkSwitchAccountSelectors; private System.Windows.Forms.CheckBox checkSwitchAccountSelectors;
private System.Windows.Forms.Label labelUI; private System.Windows.Forms.Label labelUI;
private System.Windows.Forms.Panel panelUI; private System.Windows.Forms.Panel panelUI;
private System.Windows.Forms.Label labelTray;
private System.Windows.Forms.Panel panelUpdates; private System.Windows.Forms.Panel panelUpdates;
private System.Windows.Forms.Panel panelTray;
private System.Windows.Forms.Label labelUpdates; private System.Windows.Forms.Label labelUpdates;
private System.Windows.Forms.CheckBox checkBestImageQuality; private System.Windows.Forms.CheckBox checkBestImageQuality;
private System.Windows.Forms.CheckBox checkOpenSearchInFirstColumn;
} }
} }

View File

@@ -14,38 +14,27 @@ namespace TweetDuck.Core.Other.Settings{
this.updates.CheckFinished += updates_CheckFinished; this.updates.CheckFinished += updates_CheckFinished;
Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished; Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished;
comboBoxTrayType.Items.Add("Disabled");
comboBoxTrayType.Items.Add("Display Icon Only");
comboBoxTrayType.Items.Add("Minimize to Tray");
comboBoxTrayType.Items.Add("Close to Tray");
comboBoxTrayType.Items.Add("Combined");
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior, 0), comboBoxTrayType.Items.Count-1);
toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue)); toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue));
trackBarZoom.SetValueSafe(Config.ZoomLevel); trackBarZoom.SetValueSafe(Config.ZoomLevel);
labelZoomValue.Text = trackBarZoom.Value+"%"; labelZoomValue.Text = trackBarZoom.Value+"%";
checkExpandLinks.Checked = Config.ExpandLinksOnHover; checkExpandLinks.Checked = Config.ExpandLinksOnHover;
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors; checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
checkBestImageQuality.Checked = Config.BestImageQuality; checkBestImageQuality.Checked = Config.BestImageQuality;
checkSpellCheck.Checked = Config.EnableSpellCheck; checkSpellCheck.Checked = Config.EnableSpellCheck;
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
checkUpdateNotifications.Checked = Config.EnableUpdateCheck; checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
} }
public override void OnReady(){ public override void OnReady(){
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged; checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged; checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged;
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged; checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged; checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged;
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged; trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
comboBoxTrayType.SelectedIndexChanged += comboBoxTrayType_SelectedIndexChanged;
checkTrayHighlight.CheckedChanged += checkTrayHighlight_CheckedChanged;
checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged; checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged;
btnCheckUpdates.Click += btnCheckUpdates_Click; btnCheckUpdates.Click += btnCheckUpdates_Click;
} }
@@ -62,6 +51,10 @@ namespace TweetDuck.Core.Other.Settings{
Config.SwitchAccountSelectors = checkSwitchAccountSelectors.Checked; Config.SwitchAccountSelectors = checkSwitchAccountSelectors.Checked;
} }
private void checkOpenSearchInFirstColumn_CheckedChanged(object sender, EventArgs e){
Config.OpenSearchInFirstColumn = checkOpenSearchInFirstColumn.Checked;
}
private void checkBestImageQuality_CheckedChanged(object sender, EventArgs e){ private void checkBestImageQuality_CheckedChanged(object sender, EventArgs e){
Config.BestImageQuality = checkBestImageQuality.Checked; Config.BestImageQuality = checkBestImageQuality.Checked;
} }
@@ -79,15 +72,6 @@ namespace TweetDuck.Core.Other.Settings{
} }
} }
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
}
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
Config.EnableTrayHighlight = checkTrayHighlight.Checked;
}
private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e){ private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e){
Config.EnableUpdateCheck = checkUpdateNotifications.Checked; Config.EnableUpdateCheck = checkUpdateNotifications.Checked;
} }

View File

@@ -0,0 +1,116 @@
namespace TweetDuck.Core.Other.Settings {
partial class TabSettingsTray {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
this.panelTray = new System.Windows.Forms.Panel();
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
this.labelTrayIcon = new System.Windows.Forms.Label();
this.labelTray = new System.Windows.Forms.Label();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.panelTray.SuspendLayout();
this.SuspendLayout();
//
// panelTray
//
this.panelTray.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelTray.Controls.Add(this.checkTrayHighlight);
this.panelTray.Controls.Add(this.comboBoxTrayType);
this.panelTray.Controls.Add(this.labelTrayIcon);
this.panelTray.Location = new System.Drawing.Point(9, 31);
this.panelTray.Name = "panelTray";
this.panelTray.Size = new System.Drawing.Size(322, 76);
this.panelTray.TabIndex = 1;
//
// checkTrayHighlight
//
this.checkTrayHighlight.AutoSize = true;
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 56);
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
this.checkTrayHighlight.Name = "checkTrayHighlight";
this.checkTrayHighlight.Size = new System.Drawing.Size(103, 17);
this.checkTrayHighlight.TabIndex = 2;
this.checkTrayHighlight.Text = "Enable Highlight";
this.toolTip.SetToolTip(this.checkTrayHighlight, "Highlights the tray icon if there are new tweets.\r\nOnly works for columns with popup or audio notifications.\r\nThe icon resets when the main window is restored.");
this.checkTrayHighlight.UseVisualStyleBackColor = true;
//
// comboBoxTrayType
//
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxTrayType.FormattingEnabled = true;
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 5);
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 5, 3, 3);
this.comboBoxTrayType.Name = "comboBoxTrayType";
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 21);
this.comboBoxTrayType.TabIndex = 0;
this.toolTip.SetToolTip(this.comboBoxTrayType, "Changes behavior of the Tray icon.\r\nRight-click the icon for an action menu.");
//
// labelTrayIcon
//
this.labelTrayIcon.AutoSize = true;
this.labelTrayIcon.Location = new System.Drawing.Point(3, 38);
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
this.labelTrayIcon.Name = "labelTrayIcon";
this.labelTrayIcon.Size = new System.Drawing.Size(52, 13);
this.labelTrayIcon.TabIndex = 1;
this.labelTrayIcon.Text = "Tray Icon";
//
// labelTray
//
this.labelTray.AutoSize = true;
this.labelTray.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelTray.Location = new System.Drawing.Point(6, 8);
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
this.labelTray.Name = "labelTray";
this.labelTray.Size = new System.Drawing.Size(96, 20);
this.labelTray.TabIndex = 0;
this.labelTray.Text = "System Tray";
//
// TabSettingsTray
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.panelTray);
this.Controls.Add(this.labelTray);
this.Name = "TabSettingsTray";
this.Size = new System.Drawing.Size(340, 119);
this.panelTray.ResumeLayout(false);
this.panelTray.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Panel panelTray;
private System.Windows.Forms.CheckBox checkTrayHighlight;
private System.Windows.Forms.ComboBox comboBoxTrayType;
private System.Windows.Forms.Label labelTrayIcon;
private System.Windows.Forms.Label labelTray;
private System.Windows.Forms.ToolTip toolTip;
}
}

View File

@@ -0,0 +1,33 @@
using System;
namespace TweetDuck.Core.Other.Settings{
sealed partial class TabSettingsTray : BaseTabSettings{
public TabSettingsTray(){
InitializeComponent();
comboBoxTrayType.Items.Add("Disabled");
comboBoxTrayType.Items.Add("Display Icon Only");
comboBoxTrayType.Items.Add("Minimize to Tray");
comboBoxTrayType.Items.Add("Close to Tray");
comboBoxTrayType.Items.Add("Combined");
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior, 0), comboBoxTrayType.Items.Count-1);
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
}
public override void OnReady(){
comboBoxTrayType.SelectedIndexChanged += comboBoxTrayType_SelectedIndexChanged;
checkTrayHighlight.CheckedChanged += checkTrayHighlight_CheckedChanged;
}
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
}
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
Config.EnableTrayHighlight = checkTrayHighlight.Checked;
}
}
}

View File

@@ -21,7 +21,7 @@ namespace TweetDuck{
public const string BrandName = "TweetDuck"; public const string BrandName = "TweetDuck";
public const string Website = "https://tweetduck.chylex.com"; public const string Website = "https://tweetduck.chylex.com";
public const string VersionTag = "1.9.1"; public const string VersionTag = "1.10.1";
public static readonly bool IsPortable = File.Exists("makeportable"); public static readonly bool IsPortable = File.Exists("makeportable");

View File

@@ -1,11 +1,64 @@
# Support
[Follow TweetDuck on Twitter](https://twitter.com/TryTweetDuck) &nbsp;|&nbsp; [Support via PayPal](https://paypal.me/chylex) &nbsp;|&nbsp; [Support via Patreon](https://www.patreon.com/chylex)
<a target='_blank' rel='nofollow' href='https://app.codesponsor.io/link/2W76v7hRmUe7NfaeUKq9K8Wq/chylex/TweetDuck'>
<img alt='Sponsor' width='888' height='68' src='https://app.codesponsor.io/embed/2W76v7hRmUe7NfaeUKq9K8Wq/chylex/TweetDuck.svg' />
</a>
# Build Instructions # Build Instructions
The program was built using Visual Studio 2017. After opening the solution, make sure you have **CefSharp.WinForms** and **Microsoft.VC120.CRT.JetBrains** included - if not, download them using NuGet. ### Setup
The program was built using Visual Studio 2017. Before opening the solution, please make sure you have the following workloads and components installed (optional components that are not listed can be deselected to save space):
* **.NET desktop development**
* .NET Framework 4 4.6 development tools
* **Desktop development with C++**
* VC++ 2017 v141 toolset
After opening the solution, download the following NuGet packages by right-clicking on the solution and selecting **Restore NuGet Packages**, or manually running these commands in the **Package Manager Console**:
``` ```
PM> Install-Package CefSharp.WinForms -Version 57.0.0 PM> Install-Package CefSharp.WinForms -Version 57.0.0
PM> Install-Package Microsoft.VC120.CRT.JetBrains PM> Install-Package Microsoft.VC120.CRT.JetBrains
``` ```
When building the `Release` configuration, the folder will only contain files intended for distribution (no debug symbols or other unnecessary files). You may package the files yourself, or use `bld/RUN BUILD.bat` to generate installer files using Inno Setup (make sure the Inno Setup binaries are on your PATH). ### Debug
Built files are available in **bin/x86**, installer files are generated in **bld/Output**. If you decide to release a custom version publicly, please make it clear that it is not the original TweetDuck. It is recommended to create a separate data folder for debugging, otherwise you will not be able to run TweetDuck while debugging the solution.
To do that, open **TweetDuck Properties**, click the **Debug** tab, make sure your **Configuration** is set to `Active (Debug)` (or just `Debug`), and insert this into the **Command line arguments** field:
```
-datafolder TweetDuckDebug
```
### Build
To make a release build of TweetDuck, open **Batch Build**, tick all `Release` configurations except for the `UnitTest` project (otherwise the build will fail), and click **Rebuild**. Check the status bar to make sure it says **Rebuild All succeeded**; if not, open the **Output** view and see which part of the build failed.
After the build succeeds, the **bin/x86/Release** folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#Installers) section for automated installer generation.
If you decide to release a custom version publicly, please make it clear that it is not an official release of TweetDuck.
### Installers
TweetDuck uses **Inno Setup** to automate the creation of installers. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin).
Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place.
Now you can generate installers after a build by running **bld/RUN BUILD.bat**. Note that despite the name, this will only package the files, you still need to run the [build](#Build) in Visual Studio!
After the window closes, three installers will be generated inside the **bld/Output** folder:
* **TweetDuck.exe**
* This is the main installer that creates entries in the Start Menu & Programs and Features, and an optional desktop icon
* **TweetDuck.Update.exe**
* This is a lightweight update installer that only contains the most important files that usually change across releases
* It will automatically download and apply the full installer if the user's current version of CEF does not match (the download link is in `gen_upd.iss` and points to this repository by default)
* **TweetDuck.Portable.exe**
* This is a portable installer that does not need administrator privileges
* It automatically creates a `makeportable` file in the program folder, which forces TweetDuck to run in portable mode
Note: There is a small chance you will see a resource error when running `RUN BUILD.bat`. If that happens, close the console window (which will terminate all Inno Setup processes and leave corrupted installer files in the output folder), and run it again.
### Code Notes
There are many references to the official TweetDuck website and this repository in the code and installers, so if you plan to release your own version, make sure to search for `tweetduck.chylex.com` and `github.com` in the whole repository and replace them appropriately.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

404
Resources/Guide/index.html Normal file
View File

@@ -0,0 +1,404 @@
<!DOCTYPE html>
<html>
<head>
<title>TweetDuck - Guide</title>
<meta charset="utf-8">
<meta name="robots" content="index,follow">
<meta name="author" content="chylex">
<meta name="description" content="TweetDuck guide">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="img/icon.ico">
<link rel="stylesheet" href="style.css" type="text/css">
<script type="text/javascript" src="script.js" async></script>
</head>
<body>
<div id="guide">
<section>
<article>
<h2>General</h2>
<details>
<summary id="main-menu">How to open the main menu</summary>
<div>
<p>The main menu gives you access to the following items:</p>
<img src="img/app-menu.png" alt="">
<p>There are two ways to open the main menu:</p>
<ul>
<li>Click <em>Settings</em> in the left panel, then select <em>TweetDuck</em></li>
<li><em>Right-click anywhere</em> and you will either see the listed options, or a <em>TweetDuck</em> entry that contains these options</li>
</ul>
<img src="img/settings-dropdown.png" alt="">
</div>
</details>
<details>
<summary id="customize-theme">How to customize the theme and layout</summary>
<div>
<ol>
<li>Click <em>Settings</em> in the left panel</li>
<li>Continue to <em>Edit layout &amp; design</em></li>
<li>Now you can customize many aspects of the website; note that unlike the default TweetDeck settings, the column width and font size can be configured here in much greater detail</li>
</ol>
<img src="img/settings-editdesign.png" alt="">
<p>This is done using an official plugin called <em>Edit layout &amp; design</em>, which is enabled by default. If the plugin is disabled, you can still access the default TweetDeck settings:</p>
<ol>
<li>Click <em>Settings</em> on the bottom left</li>
<li>Continue to <em>Settings</em> (again)</li>
<li>Here you can customize the theme, column width, and font size</li>
</ol>
<img src="img/settings-dropdown.png" alt="">
</div>
</details>
<details>
<summary id="emoji">How to add emoji to tweets</summary>
<div>
<p>When writing a new tweet, click the heart icon to open an emoji picker. If you're writing a reply, click the <em>Popout</em> icon to bring the reply into the New Tweet panel.</p>
<p>Then you can immediately type into the <em>Search</em> field, which accepts keywords separated by space. Pressing <em>Enter</em> in the search field (when not empty) will insert the first result into your tweet. Pressing <em>Escape</em> closes the emoji picker.</p>
<p>You can also use your mouse to change the skin tone, scroll through the emoji, and click on the emoji to insert them.</p>
<p>Typing <em>:emoji_name:</em> in the New Tweet panel will automatically search for an emoji using those keywords. If a single emoji is found, it will be inserted into the tweet (press <em>Backspace</em> or <em>Escape</em> to revert it). If multiple emojis are found, it will open the emoji picker where you can refine the search, or press <em>Escape</em> to go back.</p>
<p>Emoji are provided by an official plugin called <em>Emoji keyboard</em>, which is enabled by default. The heart icon will not show if the plugin is disabled.</p>
<img src="img/new-tweet-emoji.png" alt="">
</div>
</details>
<details>
<summary id="templates">How to use tweet templates</summary>
<div>
<p>To create a simple template to use when writing a new tweet or reply:</p>
<ol>
<li>Click <em>Manage templates</em> in the New Tweet panel; if you're writing a reply, click the <em>Popout</em> icon first to bring the reply into the large panel</li>
<li>Click <em>New template</em> on the bottom right</li>
<li>Fill in the template name and contents</li>
<li>Click <em>Confirm</em> to create the template</li>
</ol>
<p>After you create a template, it will be added to the list. There are two icons next to each entry:</p>
<ul>
<li>Click the <em>pencil icon to edit</em> the template</li>
<li>Click the <em>cross icon to delete</em> the template</li>
</ul>
<p>To use the template, <em>click the template name</em> to replace your current tweet text with the template, or click while holding <em>Shift</em> to append the template to your tweet instead. You can use the Shift+click functionality to quickly chain multiple templates.</p>
<img src="img/new-tweet-template-basic.png" alt="">
<p>When writing a template, you can use special <em>tokens</em> listed in the <em>Advanced</em> section. Here is an example of one of the tokens:</p>
<img src="img/new-tweet-template-advanced.png" alt="">
</div>
</details>
<details>
<summary id="upload-from-clipboard">How to upload images from clipboard</summary>
<div>
<p>When writing a tweet/reply, press <em>Ctrl+V</em> to upload an image from clipboard. You can use this to quickly paste a selection from an image editor such as Paint, or after copying an image in your browser.</p>
<p>Make sure you're in the tweet input field before you press Ctrl+V, otherwise the keyboard shortcut won't trigger.</p>
<p>Note that this will only work when your clipboard contains the image itself; it will not work if you copy a file or URL.</p>
</div>
</details>
</article>
<article>
<h2>Columns</h2>
<details>
<summary id="copy-tweet-links">How to copy links to tweets</summary>
<div>
<p>When you right-click anywhere inside a tweet, you will be given these options:</p>
<ul>
<li>Open tweet in browser</li>
<li><em>Copy tweet address</em></li>
<li>Screenshot tweet to clipboard</li>
</ul>
<p>If the tweet contains a quote, you will also be able to directly open the quote or copy its address.</p>
<p>Note that these options will not appear when you right-click a private message.</p>
</div>
</details>
<details>
<summary id="download-media">How to download images and videos</summary>
<div>
<p>When you right-click an image or a video thumbnail with a purple play button, you will be given these options:</p>
<ul>
<li>Open image/video in browser</li>
<li>Copy image/video address</li>
<li><em>Save image/video as...</em></li>
</ul>
<p>TweetDuck will attempt to fetch the highest quality image when you click any of these options. You can disable that by going to the <em>main menu</em>, selecting <em>Options</em>, and then unchecking <em>Best Image Quality</em>.</p>
<p>Whenever possible, the username and quality are included in the filename by default, for convenience. After you select a folder and click Save, the image/video will be downloaded in the background. There is no notification for when the download finishes, but you will be notified if it fails.</p>
</div>
</details>
<details>
<summary id="screenshot-tweets">How to take screenshots of individual tweets</summary>
<div>
<p>When you right-click anywhere inside a tweet, you will be given these options:</p>
<ul>
<li>Open tweet in browser</li>
<li>Copy tweet address</li>
<li><em>Screenshot tweet to clipboard</em></li>
</ul>
<p>Taking a screenshot may take several seconds, especially if it contains large images or previews. After taking a screenshot, you can paste it into an image editor such as Paint.</p>
<p>Note that these options will not appear when you right-click a private message.</p>
</div>
</details>
<details>
<summary id="clear-columns">How to clear and restore column contents</summary>
<div>
<p>TweetDeck lets you clear a column, which can help keep things organized by hiding tweets you have already read. To clear a column, click the <em>slider icon</em> on top of the column, and then click the <em>Clear</em> button.</p>
<p>If you need to revert this decision and restore a column, normally you would need to delete and re-create it, but in TweetDuck you can simply hold Shift which turns the Clear button into a <em>Restore</em> button.</p>
<img src="img/column-clear-preferences.png" alt="">
<p>If you clear columns frequently, you can enable an official plugin that lets you clear columns much quicker:</p>
<ol>
<li>Open the <em>main menu</em>, and select <em>Plugins</em> to open the list of available plugins</li>
<li>Find an entry that says <em>Clear columns</em>, it will be somewhere near the bottom as the plugin is disabled by default</li>
<li>Click <em>Enable</em> on the right side to enable the plugin and reload the browser</li>
</ol>
<p>Now you can clear...</p>
<ul>
<li>...a single column by clicking the <em>droplet icon</em> on top of each column</li>
<li>...a single column by holding the <em>1-9 number key</em> and pressing <em>Delete</em></li>
<li>...all columns by clicking <em>Clear columns</em> in the left panel</li>
<li>...all columns by pressing <em>Alt+Delete</em></li>
</ul>
<img src="img/column-clear-header.png" alt="">
<p>As mentioned before, hold Shift with any of these to restore the columns instead. Note that some keyboard layouts require using the Shift key when pressing number keys; if that is the case for you, please use the numpad or mouse instead.</p>
</div>
</details>
</article>
<article>
<h2>Notifications</h2>
<details>
<summary id="enable-notifications">How to enable desktop or sound notifications</summary>
<div>
<p>New columns have disabled notifications by default. To enable them, click the <em>slider icon</em> on top of the column, and expand the <em>Preferences</em> section.</p>
<p>Now you can toggle either, or both of the notification options:</p>
<img src="img/column-preferences.png" alt="">
</div>
</details>
<details>
<summary id="move-resize-notifications">How to move or resize desktop notifications</summary>
<div>
<p>Open the <em>main menu</em>, select <em>Options</em>, and then click the <em>Notifications</em> tab. Here, you can customize many aspects of desktop notifications.</p>
<p>Scroll down to the <em>Location</em> section where you can customize where the notification shows up. You can either pick one of the 4 corners of your screen and the distance from the corner, or select <em>Custom</em> and then you'll be able to freely move the example notification window.</p>
<img src="img/options-notifications-location.png" alt="">
<p>Scroll down to the <em>Size</em> section to customize the size of the notification window. By default, TweetDuck sets the size based on your font size setting, the zoom level you can customize in the General tab, and your system DPI. If you pick <em>Custom</em>, you will be able to freely resize the example notification window.</p>
<img src="img/options-notifications-size.png" alt="">
<p>Note that moving and resizing the notification only works while you're inside the Options dialog, that is to prevent accidental clicks messing up your settings.</p>
</div>
</details>
<details>
<summary id="sound-notifications">How to customize sound notifications</summary>
<div>
<p>Open the <em>main menu</em>, select <em>Options</em>, and then click the <em>Sounds</em> tab. Here, you can pick a sound file that will be used instead of the default TweetDeck sound notification.</p>
<p>Keep in mind that you're only linking to the sound file, so make sure not to delete the file afterwards, otherwise TweetDuck won't find it anymore.</p>
<img src="img/options-sounds.png" alt="">
<p>If you're unable to select MP3 files or other common audio file types, please ensure that you have Windows Media Player installed on your system, otherwise you will only be able to select basic WAV files.</p>
</div>
</details>
<details>
<summary id="mute-notifications">How to temporarily mute all notifications</summary>
<div>
<p>There are two ways you can mute/unmute notifications:</p>
<ul>
<li>Open the <em>main menu</em> and click <em>Mute notifications</em></li>
<li>If you've enabled the <em>tray icon</em>, right-click it and then click <em>Mute notifications</em></li>
</ul>
<p>The option persists across restarts &ndash; if you mute notifications and then restart TweetDuck, don't forget to unmute the notifications again.</p>
<p>Unmuting notifications will display all missed desktop notifications (unless TweetDuck was restarted in the meantime).</p>
</div>
</details>
</article>
</section>
<section>
<article>
<h2>Options</h2>
<details>
<summary id="configure-tweetduck">How to configure TweetDuck</summary>
<div>
<p>Open the <em>main menu</em> and select <em>Options</em>. Here you can configure various parts of TweetDuck; the dialog is split into several tabs:</p>
<ul>
<li><em>General</em> tab for user interface, zoom, and update options</li>
<li><em>System Tray</em> tab to enable and configure the tray icon</li>
<li><em>Notifications</em> tab to configure desktop notifications</li>
<li><em>Sounds</em> tab to set a custom sound notification</li>
<li><em>Advanced</em> tab for highly technical options</li>
</ul>
<p>You can move your cursor over most options to display a <em>tooltip with a detailed explanation</em> of what that option does.</p>
</div>
</details>
<details>
<summary id="manage-plugins">How to view and manage plugins</summary>
<div>
<p>TweetDuck has several offical plugins that extend the website and notifications with new functionality.</p>
<p>Open the <em>main menu</em> and select <em>Plugins</em> to open the plugin list. Here you can see what each plugin does, and enable/disable them individually. </p>
<p>If you want to install a custom plugin, click <em>Open Plugin Folder</em>. A plugin is a folder that contains a <em>.meta</em> file and several others, make sure you copy and paste the folder itself into the opened plugin folder. To verify that you installed it correctly, click <em>Reload All</em> (which also reloads the website) and the plugin should appear.</p>
<p>Please, be careful when installing new plugins, and ensure that you get them from trustworthy sources. If you're unsure about a plugin, feel free to <a href="https://github.com/chylex/TweetDuck/issues/new" rel="nofollow">create an issue</a> and upload the plugin there (<a href="https://github.com" rel="nofollow">GitHub</a> account required).</p>
</div>
</details>
<details>
<summary id="manage-profile">How to backup your profile or move it to another computer</summary>
<div>
<ol>
<li>Open the <em>main menu</em>, select <em>Options</em>, and click <em>Manage Options</em> on the bottom left</li>
<li>Select <em>Export profile</em> and proceed with <em>Next</em></li>
<li>Select items you want to save in your profile (note that <em>Plugin Data</em> includes data from official plugins, such as those that let you customize the website or create tweet templates)</li>
<li>Click <em>Export Profile</em></li>
</ol>
<img src="img/options-manage.png" alt="" style="margin-right:6px">
<img src="img/options-manage-export.png" alt="">
<p>You can save your profile into a cloud storage (Dropbox, Google Drive, etc.) or an external drive, for example. When you want to restore it, follow the same steps but select <em>Import profile</em> and then select the file instead.</p>
<p>When importing a profile, you will be again able to pick which items you want to restore. You can for example export a full profile including your login session, but then only import program options and plugin data if you want to login to a different account.</p>
</div>
</details>
<details>
<summary id="restore-options">How to restore default options</summary>
<div>
<ol>
<li>Open the <em>main menu</em>, select <em>Options</em>, and click <em>Manage Options</em> on the bottom left</li>
<li>Select <em>Restore defaults</em> and proceed with <em>Next</em></li>
<li>Select items you want reset (note that <em>Plugin Data</em> includes data from official plugins, such as those that let you customize the website or create tweet templates)</li>
<li>Click <em>Restore Defaults</em></li>
</ol>
<img src="img/options-manage.png" alt="" style="margin-right:6px">
<img src="img/options-manage-reset.png" alt="">
</div>
</details>
</article>
<article>
<h2>Efficiency</h2>
<details>
<summary id="keyboard-shortcuts">How to use keyboard shortcuts</summary>
<div>
<ol>
<li>Click <em>Settings</em> in the left panel</li>
<li>Continue to <em>Keyboard shortcuts</em></li>
<li>Here you can see most available keyboard shortcuts you can use in the browser window</li>
</ol>
<img src="img/settings-dropdown.png" alt="">
<p>You can also often hold Ctrl or Shift to trigger alternative actions:</p>
<ul>
<li>When <em>selecting accounts</em>, hold <em>Shift</em> to select multiple accounts (can be configured in the Options)</li>
<li>When <em>clearing columns</em>, hold <em>Shift</em> to restore the column instead</li>
<li>When <em>clicking video thumbnails</em>, hold <em>Ctrl</em> to open them in your browser</li>
</ul>
<p>Finally, if you click into a desktop notification window, you can use these keyboard shortcuts:</p>
<ul>
<li><em>Enter</em> to skip the current notification</li>
<li><em>Escape</em> to close the window (skips all notifications in the queue)</li>
<li><em>Space</em> to pause/unpause the timer</li>
</ul>
</div>
</details>
<details>
<summary id="extra-mouse-buttons">How to use the forward / back mouse buttons</summary>
<div>
<p>If you have a mouse that supports the forward and back buttons, you can use them in both the browser and a desktop notification. All you need to do is move the cursor over the window (even if it's not focused), and press one of the buttons.</p>
<p>In the browser:</p>
<ul>
<li>Press <em>forward</em> over a <em>tweet to open it in detail view</em> (unlike clicking, this will not trigger links or media thumbnails)</li>
<li>Press <em>back</em> anywhere to <em>close modal dialogs</em> or the <em>New Tweet panel</em>, or over a <em>column to return back from detail view</em> (if there are no dialogs or panels open, pressing the button outside a column will trigger it for all columns at once)</li>
</ul>
<p>In the desktop notification:</p>
<ul>
<li>Press <em>forward</em> to skip the current notification</li>
<li>Press <em>back</em> to close the window (skips all notifications in the queue)</li>
</ul>
</div>
</details>
<details>
<summary id="pin-new-tweet">How to keep the New Tweet panel open</summary>
<div>
<p>Open the New Tweet panel, and click the <em>pin icon</em> on top. When the pin points to the left, the panel will stay open after tweeting or restarting TweetDuck.</p>
<img src="img/new-tweet-pin.png" alt="">
</div>
</details>
<details>
<summary id="popout-replies">How to instantly popout replies</summary>
<div>
<p><em>Middle-click the reply icon</em> to instantly open your reply in the New Tweet panel.</p>
<p>Middle-clicks are usually done by pressing your mouse wheel as if it was a button. When using a laptop touchpad or certain mice, the ways of triggering a middle click vary.</p>
</div>
</details>
<details>
<summary id="reply-account">How to change which account will be pre-selected for replies</summary>
<div>
<p>By default, TweetDeck pre-selects the account mentioned in the column header. The ability to change this is provided by an official plugin which is disabled by default, as it's a bit more difficult to setup, but it can be very powerful. To enable the plugin:</p>
<ol>
<li>Open the <em>main menu</em> and select <em>Plugins</em></li>
<li>Find an entry that says <em>Custom reply account</em>, it will be somewhere near the bottom as the plugin is disabled by default</li>
<li>Click <em>Enable</em> on the right side to enable the plugin</li>
</ol>
<p>After you enable the plugin, it will use your preferred account for all replies by default. If that's your intention, you can simply enable the plugin and leave it, otherwise continue reading:</p>
<ol>
<li>Click <em>Configure</em> next to the plugin to open a folder with the configuration file
<li>Open <em>configuration.js</em> in a text editor that can edit and save JavaScript or any pure text files, therefore office suits or WordPad are not suitable; if you don't have any specific editor, use Notepad.</li>
<li>The configuration file includes very detailed instructions &ndash; you can use one of the <em>presets</em>, a <em>specific account</em> for all replies, or use JavaScript to <em>fully customize</em> the reply behavior</li>
</ol>
<p>After editing the configuration, return back to Plugins and click <em>Reload All</em> on the bottom left. Now you can close Plugins and test if replies work the way you want.</p>
<p>Note that this will not affect the Messages column, that one will always pre-select the account which received the private message.</p>
</div>
</details>
</article>
<article>
<h2>Advanced</h2>
<details>
<summary id="dev-tools">How to open Chrome Dev Tools</summary>
<div>
<ol>
<li>Open the <em>main menu</em>, select <em>Options</em>, and click the <em>Advanced</em> tab</li>
<li>Click <em>Open Program Folder</em></li>
<li>Download <a href="https://github.com/chylex/TweetDuck/raw/master/bld/Resources/devtools_resources.pak" rel="nofollow">devtools_resources.pak</a> and place it into the opened folder</li>
<li>Click <em>Restart the Program</em></li>
<li>Now, open the <em>main menu</em> again and you should see <em>Open dev tools</em>; you can also right-click inside a notification and see the same option (make sure to pause the notification first by clicking Freeze in the context menu)</li>
</ol>
</div>
</details>
<details>
<summary id="custom-css">How to customize styles using CSS</summary>
<div>
<ol>
<li>Open the <em>main menu</em>, select <em>Options</em>, and click the <em>Advanced</em> tab</li>
<li>Click <em>Edit CSS</em></li>
</ol>
<p>Now you can write custom CSS into the <em>Browser</em> and <em>Notification</em> sections.</p>
<p>Note that the Browser section will immediately take effect as you type. You can also still access the browser and Dev Tools, as the dialog does not block the browser window.</p>
<p>Basic knowledge of HTML and CSS is recommended. Mozilla Development Network has a huge library of resources on both <a href="https://developer.mozilla.org/en-US/Learn/Getting_started_with_the_web/HTML_basics" rel="nofollow">HTML</a> and <a href="https://developer.mozilla.org/en-US/Learn/Getting_started_with_the_web/CSS_basics" rel="nofollow">CSS</a>.</p>
</div>
</details>
<details>
<summary id="plugin-development">How to develop plugins</summary>
<div>
<p>Before creating a plugin, you should have at least basic knowledge of <a href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web" rel="nofollow">web development</a> (namely HTML, CSS, JavaScript), and several JS libraries TweetDeck uses, such as <a href="https://learn.jquery.com" rel="nofollow">jQuery 2</a>, <a href="https://mustache.github.io" rel="nofollow">Mustache</a>, <a href="https://github.com/ded/klass" rel="nofollow">klass</a>, and <a href="https://flightjs.github.io/" rel="nofollow">Flight</a>.</p>
<p>Working with the TweetDeck source code involves a lot of reverse-engineering. You can visit <a href="https://github.com/DeckHack/discoveries" rel="nofollow">DeckHack</a> which is working to document its source code, and view <a href="https://github.com/chylex/TweetDuck/tree/master/Resources/Scripts" rel="nofollow">TweetDuck sources</a> which includes all scripts and official plugins.</p>
<p>Once you're ready to start creating your own plugins, visit the official <a href="https://github.com/chylex/TweetDuck/wiki/Plugins%EA%9E%89-1.-The-Basics" rel="nofollow">plugin development documentation</a> which will explain the structure of a plugin, and show you all TweetDuck-specific functionality you cannot normally use in browsers.</p>
</div>
</details>
</article>
</section>
</div>
</body>
</html>

84
Resources/Guide/script.js Normal file
View File

@@ -0,0 +1,84 @@
var init = function(){
if (!("open" in document.getElementsByTagName("details")[0])){
var elements = document.getElementsByTagName("details");
var onClick = function(e){
var summary = e.target;
var parent = e.target.parentElement;
var contents = e.target.nextElementSibling;
if (parent.hasAttribute("open")){
parent.removeAttribute("open");
summary.setAttribute("aria-expanded", "false");
contents.setAttribute("aria-hidden", "true");
contents.style.display = "none";
}
else{
parent.setAttribute("open", "");
summary.setAttribute("aria-expanded", "true");
contents.setAttribute("aria-hidden", "false");
contents.style.display = "block";
}
};
var onKey = function(e){
if (e.keyCode === 13 || e.keyCode === 32){
onClick(e);
}
};
for(var index = 0; index < elements.length; index++){
var ele = elements[index];
if (ele.childElementCount === 2){
var summary = ele.children[0];
var contents = ele.children[1];
ele.style.display = "block";
ele.setAttribute("role", "group");
summary.setAttribute("role", "button");
summary.setAttribute("aria-expanded", "false");
summary.setAttribute("tabindex", 0);
summary.addEventListener("click", onClick);
summary.addEventListener("keydown", onKey);
contents.setAttribute("aria-hidden", "true");
contents.style.display = "none";
}
}
}
else if ("WebkitAppearance" in document.documentElement.style){
var elements = document.getElementsByTagName("summary");
var onMouseDown = function(e){
e.target.classList.add("webkit-workaround");
};
var onMouseUp = function(e){
e.target.classList.remove("webkit-workaround");
e.target.blur();
};
for(var index = 0; index < elements.length; index++){
elements[index].addEventListener("mousedown", onMouseDown);
elements[index].addEventListener("mouseup", onMouseUp);
}
}
if (location.hash.length > 1){
var element = document.getElementById(location.hash.substring(1));
if (element && element.tagName === "SUMMARY"){
element.click();
element.scrollIntoView(true);
}
}
};
if (document.readyState !== "loading"){
init();
}
else{
document.addEventListener("DOMContentLoaded", init);
}

132
Resources/Guide/style.css Normal file
View File

@@ -0,0 +1,132 @@
body {
margin: 0;
font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif;
background-color: #222;
color: #ddd;
overflow-y: scroll;
}
#guide {
width: 100%;
max-width: 1440px;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
align-self: stretch;
}
#guide section {
flex: 1 1 0;
margin: 0 24px;
min-width: 360px;
display: flex;
flex-direction: column;
}
@media(max-width: 408px) {
#guide section {
min-width: calc(100vw - 48px);
}
}
#guide h2 {
margin: 20px 0 10px;
font-size: 32px;
cursor: default;
}
#guide details {
transition: background-color 0.2s ease;
}
#guide details[open] {
background-color: rgba(255, 255, 255, 0.05);
}
#guide summary {
display: inline-block;
margin: 0 0 11px;
padding: 4px 10px;
font-size: 19px;
font-weight: bold;
color: #afdfff;
cursor: pointer;
opacity: 0.8;
transition: opacity 0.15s ease;
}
#guide summary::-webkit-details-marker {
display: none;
}
#guide summary.webkit-workaround {
outline: none;
}
#guide summary::before {
content: "▶";
display: inline-block;
font-size: 13px;
vertical-align: top;
margin-right: 6px;
padding-top: 2px;
transform-origin: 35% 50%;
transition: transform 0.1s ease;
}
#guide summary:hover, #guide details[open] summary {
opacity: 1;
}
#guide details[open] summary::before {
transform: rotateZ(90deg) translate(0.5px, -3px);
}
#guide details > div {
margin-bottom: 22px;
padding: 0 25px 20px;
font-size: 17px;
}
#guide details > div > *:first-child {
margin-top: 0;
}
#guide details > div > *:last-child {
margin-bottom: 0;
}
em {
color: #f8d88b;
font-style: normal;
}
a {
color: #8bc6f8;
opacity: 0.85;
}
a:hover {
opacity: 1;
}
p, li {
line-height: 130%;
}
ul {
padding-left: 26px;
}
ol {
padding-left: 18px;
}
li {
margin-bottom: 6px;
}
img {
border: 2px groove rgba(255, 255, 255, 0.6);
max-width: 100%;
}

View File

@@ -8,10 +8,10 @@ Edit layout & design
chylex chylex
[version] [version]
1.1.2 1.1.4
[website] [website]
https://tweetduck.chylex.com https://tweetduck.chylex.com
[requires] [requires]
1.7 1.10.2

View File

@@ -108,7 +108,7 @@ enabled(){
let menu = $(".js-dropdown-content").children("ul").first(); let menu = $(".js-dropdown-content").children("ul").first();
return if menu.length === 0; return if menu.length === 0;
let itemTD = menu.children("[data-std]").first(); let itemTD = menu.children("[data-tweetduck]").first();
return if itemTD.length === 0; return if itemTD.length === 0;
if (!itemTD.prev().hasClass("drp-h-divider")){ if (!itemTD.prev().hasClass("drp-h-divider")){
@@ -116,7 +116,7 @@ enabled(){
} }
let itemEditDesign = $('<li class="is-selectable"><a href="#" data-action>Edit layout &amp; design</a></li>'); let itemEditDesign = $('<li class="is-selectable"><a href="#" data-action>Edit layout &amp; design</a></li>');
itemTD.after(itemEditDesign); itemEditDesign.insertAfter(itemTD);
itemEditDesign.on("click", "a", this.openEditDesignDialog); itemEditDesign.on("click", "a", this.openEditDesignDialog);
@@ -331,7 +331,7 @@ enabled(){
this.css.insert("#general_settings .cf { display: none !important }"); this.css.insert("#general_settings .cf { display: none !important }");
this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }"); this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }");
this.css.insert(".txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: "+this.config.fontSize+" !important }"); this.css.insert("html[data-td-font] { font-size: "+this.config.fontSize+" !important }");
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }"); this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
let notificationScrollbarColor = null; let notificationScrollbarColor = null;
@@ -342,12 +342,12 @@ enabled(){
this.css.insert(".app-content, .app-columns-container { background-color: #444448 }"); this.css.insert(".app-content, .app-columns-container { background-color: #444448 }");
this.css.insert(".column-drag-handle { opacity: 0.5 }"); this.css.insert(".column-drag-handle { opacity: 0.5 }");
this.css.insert(".column-drag-handle:hover { opacity: 1 }"); this.css.insert(".column-drag-handle:hover { opacity: 1 }");
this.css.insert(".scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #666 }"); this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb { background-color: #666 }");
notificationScrollbarColor = "666"; notificationScrollbarColor = "666";
break; break;
case "light": case "light":
this.css.insert(".scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #d2d6da }"); this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb { background-color: #d2d6da }");
this.css.insert(".app-columns-container.scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #a5aeb5 }"); this.css.insert(".app-columns-container.scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #a5aeb5 }");
notificationScrollbarColor = "a5aeb5"; notificationScrollbarColor = "a5aeb5";
break; break;
@@ -507,7 +507,7 @@ enabled(){
$TDP.injectIntoNotificationsBefore(this.$token, "css", "</head>", ` $TDP.injectIntoNotificationsBefore(this.$token, "css", "</head>", `
<style type='text/css'> <style type='text/css'>
.txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: ${this.config.fontSize} !important } html[data-td-font] { font-size: ${this.config.fontSize} !important }
.avatar { border-radius: ${this.config.avatarRadius}% !important } .avatar { border-radius: ${this.config.avatarRadius}% !important }
${this.config.revertIcons ? ` ${this.config.revertIcons ? `

View File

@@ -9,7 +9,7 @@ Emoji keyboard
chylex chylex
[version] [version]
1.3.1 1.4
[website] [website]
https://tweetduck.chylex.com https://tweetduck.chylex.com

View File

@@ -66,6 +66,7 @@ enabled(){
this.currentSpanner = null; this.currentSpanner = null;
var wasSearchFocused = false; var wasSearchFocused = false;
var lastEmojiKeyword, lastEmojiPosition, lastEmojiLength;
var hideKeyboard = (refocus) => { var hideKeyboard = (refocus) => {
$(this.currentKeyboard).remove(); $(this.currentKeyboard).remove();
@@ -81,8 +82,14 @@ enabled(){
$(".emoji-keyboard-popup-btn").removeClass("is-selected"); $(".emoji-keyboard-popup-btn").removeClass("is-selected");
if (refocus){ if (refocus){
$(".js-compose-text", ".js-docked-compose").focus(); this.composeInput.focus();
if (lastEmojiKeyword && lastEmojiPosition === 0){
document.execCommand("insertText", false, lastEmojiKeyword);
} }
}
lastEmojiKeyword = null;
}; };
var generateEmojiHTML = skinTone => { var generateEmojiHTML = skinTone => {
@@ -229,7 +236,7 @@ enabled(){
}; };
var insertEmoji = (src, alt) => { var insertEmoji = (src, alt) => {
let input = $(".js-compose-text", ".js-docked-compose"); let input = this.composeInput;
let val = input.val(); let val = input.val();
let posStart = input[0].selectionStart; let posStart = input[0].selectionStart;
@@ -241,6 +248,8 @@ enabled(){
input[0].selectionStart = posStart+alt.length; input[0].selectionStart = posStart+alt.length;
input[0].selectionEnd = posStart+alt.length; input[0].selectionEnd = posStart+alt.length;
lastEmojiKeyword = null;
if (wasSearchFocused){ if (wasSearchFocused){
$(".emoji-keyboard-search").children("input").focus(); $(".emoji-keyboard-search").children("input").focus();
} }
@@ -270,6 +279,92 @@ enabled(){
} }
}; };
this.composeInputKeyDownEvent = function(e){
if (lastEmojiKeyword && (e.keyCode === 8 || e.keyCode === 27)){ // backspace, escape
let ele = $(this)[0];
if (ele.selectionStart === lastEmojiPosition){
ele.selectionStart -= lastEmojiLength; // selects the emoji
document.execCommand("insertText", false, lastEmojiKeyword);
e.preventDefault();
e.stopPropagation();
}
lastEmojiKeyword = null;
}
};
this.composeInputKeyPressEvent = function(e){
if (String.fromCharCode(e.which) === ':'){
let ele = $(this);
let val = ele.val();
let firstColon = val.lastIndexOf(':', ele[0].selectionStart);
return if firstColon === -1;
let search = val.substring(firstColon+1, ele[0].selectionStart);
return if !/^[a-z_]+$/i.test(search);
let keywords = search.split("_").filter(kw => kw.length > 0).map(kw => kw.toLowerCase());
return if keywords.length === 0;
let foundName = me.emojiNames.filter(name => keywords.every(kw => name.includes(kw)));
if (foundName.length === 0){
return;
}
lastEmojiKeyword = `:${search}:`;
lastEmojiPosition = lastEmojiLength = 0;
if (foundName.length === 1){
let foundIndex = me.emojiNames.indexOf(foundName[0]);
let foundEmoji;
for(let array of [ me.emojiData1, me.emojiData2[me.selectedSkinTone], me.emojiData3 ]){
let realArray = array.filter(ele => ele !== "___");
if (foundIndex >= realArray.length){
foundIndex -= realArray.length;
}
else{
foundEmoji = realArray[foundIndex];
break;
}
}
if (foundEmoji){
e.preventDefault();
ele.val(val.substring(0, firstColon)+foundEmoji+val.substring(ele[0].selectionStart));
ele[0].selectionEnd = ele[0].selectionStart = firstColon+foundEmoji.length;
ele.trigger("change");
ele.focus();
lastEmojiPosition = firstColon+foundEmoji.length;
lastEmojiLength = foundEmoji.length;
}
}
else if (foundName.length > 1){
e.preventDefault();
ele.val(val.substring(0, firstColon)+val.substring(ele[0].selectionStart));
ele[0].selectionEnd = ele[0].selectionStart = firstColon;
ele.trigger("change");
if (!me.currentKeyboard){
$(".emoji-keyboard-popup-btn").click();
}
$(".emoji-keyboard-search").children("input").focus().val("");
document.execCommand("insertText", false, keywords.join(" "));
}
}
else{
lastEmojiKeyword = null;
}
};
this.composeInputFocusEvent = function(e){ this.composeInputFocusEvent = function(e){
wasSearchFocused = false; wasSearchFocused = false;
}; };
@@ -300,15 +395,18 @@ enabled(){
ready(){ ready(){
this.composeDrawer = $("[data-drawer='compose']"); this.composeDrawer = $("[data-drawer='compose']");
this.composeInput = $(".js-compose-text", ".js-docked-compose");
this.composePanelScroller = $(".js-compose-scroller", ".js-docked-compose").first().children().first(); this.composePanelScroller = $(".js-compose-scroller", ".js-docked-compose").first().children().first();
this.composePanelScroller.on("scroll", this.composerScrollEvent); this.composePanelScroller.on("scroll", this.composerScrollEvent);
$(".emoji-keyboard-popup-btn").on("click", this.emojiKeyboardButtonClickEvent); $(".emoji-keyboard-popup-btn").on("click", this.emojiKeyboardButtonClickEvent);
$(".js-compose-text", ".js-docked-compose").on("focus", this.composeInputFocusEvent);
$(document).on("click", this.documentClickEvent); $(document).on("click", this.documentClickEvent);
$(document).on("keydown", this.documentKeyEvent); $(document).on("keydown", this.documentKeyEvent);
$(document).on("uiComposeImageAdded", this.uploadFilesEvent); $(document).on("uiComposeImageAdded", this.uploadFilesEvent);
this.composeInput.on("keydown", this.composeInputKeyDownEvent);
this.composeInput.on("keypress", this.composeInputKeyPressEvent);
this.composeInput.on("focus", this.composeInputFocusEvent);
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent); this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
// HTML generation // HTML generation
@@ -429,10 +527,12 @@ disabled(){
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent); $(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
$(".emoji-keyboard-popup-btn").remove(); $(".emoji-keyboard-popup-btn").remove();
$(".js-compose-text", ".js-docked-compose").off("focus", this.composeInputFocusEvent);
$(document).off("click", this.documentClickEvent); $(document).off("click", this.documentClickEvent);
$(document).off("keydown", this.documentKeyEvent); $(document).off("keydown", this.documentKeyEvent);
$(document).off("uiComposeImageAdded", this.uploadFilesEvent); $(document).off("uiComposeImageAdded", this.uploadFilesEvent);
this.composeInput.off("keydown", this.composeInputKeyDownEvent);
this.composeInput.off("keypress", this.composeInputKeyPressEvent);
this.composeInput.off("focus", this.composeInputFocusEvent);
this.composeDrawer.off("uiComposeTweetSending", this.composerSendingEvent); this.composeDrawer.off("uiComposeTweetSending", this.composerSendingEvent);
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache; TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;

View File

@@ -8,7 +8,7 @@ Custom reply account
chylex chylex
[version] [version]
1.2.1 1.2.3
[website] [website]
https://tweetduck.chylex.com https://tweetduck.chylex.com
@@ -20,4 +20,4 @@ configuration.js
configuration.default.js configuration.default.js
[requires] [requires]
1.3.3 1.8

View File

@@ -17,7 +17,7 @@ enabled(){
return; return;
} }
var section = data.element.closest("section.column"); var section = data.element.closest("section.js-column");
var column = TD.controller.columnManager.get(section.attr("data-column")); var column = TD.controller.columnManager.get(section.attr("data-column"));
var header = $(".column-title", section); var header = $(".column-title", section);
@@ -35,7 +35,7 @@ enabled(){
} }
try{ try{
query = configuration.customSelector(column.getColumnType(), columnTitle, columnAccount, column); query = configuration.customSelector(column.getColumnType(), columnTitle, columnAccount, column, section.hasClass("column-temp"));
}catch(e){ }catch(e){
$TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: "+e.message); $TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: "+e.message);
return; return;

View File

@@ -1,12 +1,4 @@
{ {
/*
* WARNING
* -------
*
* Make sure you are editing 'configuration.js' and not the default configuration file, as the default one will be replaced with each update.
*
*/
/* /*
* Simple way of configuring the plugin * Simple way of configuring the plugin
* ------------------------------------ * ------------------------------------
@@ -29,11 +21,14 @@
* This assumes a basic knowledge of JavaScript and jQuery. * This assumes a basic knowledge of JavaScript and jQuery.
* *
* 1. Set value of 'useAdvancedSelector' to true * 1. Set value of 'useAdvancedSelector' to true
* 2. Uncomment the 'customSelector' function, and replace the example code with your desired behavior * 2. Replace the example code in 'customSelector' function with your desired behavior
* *
* The 'customSelector' function should return a string in one of the formats supported by 'defaultAccount'. * The 'customSelector' function should return a string in one of the formats supported by 'defaultAccount'.
* If it returns anything else (for example, false or undefined), it falls back to 'defaultAccount' behavior. * If it returns anything else (for example, false or undefined), it falls back to 'defaultAccount' behavior.
* *
* When writing a custom function, you can install Dev Tools to access the browser console:
* https://tweetduck.chylex.com/guide/#dev-tools
*
* *
* The 'type' parameter is TweetDeck column type. Here is the full list of column types, note that some are * The 'type' parameter is TweetDeck column type. Here is the full list of column types, note that some are
* unused and have misleading names (for example, Home columns are 'col_timeline' instead of 'col_home'): * unused and have misleading names (for example, Home columns are 'col_timeline' instead of 'col_home'):
@@ -54,28 +49,33 @@
* contain other text (for example, the Scheduled column contains the string 'All accounts'). * contain other text (for example, the Scheduled column contains the string 'All accounts').
* *
* *
* The 'column' parameter is a TweetDeck column object. If you want to see all properties of the object, * The 'column' parameter is a TweetDeck column object. If you want to see the object structure,
* run the following code in your browser console, which will return an array containing all of your * run the following code in the console for an array containing all of your column objects:
* current column objects in order:
* TD.controller.columnManager.getAllOrdered() * TD.controller.columnManager.getAllOrdered()
* *
*
* The 'isTemporary' parameter is true if the column is not attached to the main column list,
* for example when clicking on a profile and viewing their tweets in a modal dialog.
*
*/ */
useAdvancedSelector: false, useAdvancedSelector: false,
/*customSelector: function(type, title, account, column){ customSelector: function(type, title, account, column, isTemporary){
console.info(arguments); // Prints all arguments into the console
if (type === "col_search" && title === "TweetDuck"){ if (type === "col_search" && title === "TweetDuck"){
// This is a search column that looks for 'TweetDuck' in the tweets, // This is a search column that looks for 'TweetDuck' in the tweets,
// search columns are normally linked to the preferred account // search columns are normally linked to the preferred account
// so this forces the @TryTweetDuck account to be used instead. // so this forces the @TryTweetDuck account to be used instead
return "@TryTweetDuck"; return "@TryTweetDuck";
} }
else if (type === "col_timeline" && account === "@chylexcz"){ else if (type === "col_timeline" && account === "@chylexcz"){
// This is a Home column of my test account @chylexcz, // This is a Home column of my test account @chylexcz,
// but I want to reply to tweets from my official account. // but I want to reply to tweets from my official account
return "@chylexmc"; return "@chylexmc";
} }
// otherwise returns 'undefined' which falls back to 'defaultAccount' behavior // otherwise returns 'undefined' which falls back to 'defaultAccount' behavior
}*/ }
} }

View File

@@ -11,12 +11,22 @@ function Rewrite-File{
Write-Host "Processed" $file.FullName.Substring($dir.Length) Write-Host "Processed" $file.FullName.Substring($dir.Length)
} }
ForEach($file in Get-ChildItem -Include *.js -Recurse){ ForEach($file in Get-ChildItem -Include *.js -Exclude configuration.default.js -Recurse){
$lines = Get-Content -Path $file.FullName $lines = Get-Content -Path $file.FullName
$lines = ($lines | % { $_.TrimStart() }) -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1' -Replace '(?<!\w)return(\s.*?)? if (.*?);', 'if ($2)return$1;' $lines = $lines | % { $_.TrimStart() }
$lines = $lines -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1'
$lines = $lines -Replace '(?<!\w)return(\s.*?)? if (.*?);', 'if ($2)return$1;'
,$lines | Rewrite-File $file ,$lines | Rewrite-File $file
} }
ForEach($file in Get-ChildItem -Include *.css -Recurse){
$lines = Get-Content -Path $file.FullName
$lines = $lines -Replace '\s*/\*.*?\*/', ''
$lines = $lines -Replace '^\s+(.+):\s?(.+?)(?:\s?(!important))?;$', '$1:$2$3;'
$lines = $lines -Replace '^(\S.*?) {$', '$1{'
@(($lines | Where { $_ -ne '' }) -Join ' ') | Rewrite-File $file
}
ForEach($file in Get-ChildItem -Include *.html -Recurse){ ForEach($file in Get-ChildItem -Include *.html -Recurse){
$lines = Get-Content -Path $file.FullName $lines = Get-Content -Path $file.FullName
,($lines | % { $_.TrimStart() }) | Rewrite-File $file ,($lines | % { $_.TrimStart() }) | Rewrite-File $file

View File

@@ -14,6 +14,11 @@
// //
var onAppReady = []; var onAppReady = [];
//
// Variable: DOM HTML element.
//
var doc = document.documentElement;
// //
// Variable: DOM object containing the main app element. // Variable: DOM object containing the main app element.
// //
@@ -258,47 +263,62 @@
})(); })();
// //
// Function: Retrieves the tags to be put into <head> for notification HTML code. // Block: Hook into settings object to detect when the settings change, and update html attributes and notification layout.
// //
var getNotificationHeadContents = function(){ (function(){
let tags = []; let refreshSettings = function(){
let fontSizeName = TD.settings.getFontSize();
let themeName = TD.settings.getTheme();
$(document.head).children("link[rel='stylesheet']:not([title]),link[title='"+TD.settings.getTheme()+"'],meta[charset],meta[http-equiv]").each(function(){ let tags = [
`<html class='os-windows ${(doc.getAttribute("class").match(/txt-\S+/) || [ "txt-size--14" ])[0]}' data-td-font='${fontSizeName}' data-td-theme='${themeName}'><head>`
];
$(document.head).children("link[rel='stylesheet']:not([title]),link[title='"+themeName+"'],meta[charset],meta[http-equiv]").each(function(){
tags.push($(this)[0].outerHTML); tags.push($(this)[0].outerHTML);
}); });
tags.push("<style type='text/css'>"); tags.push("<style type='text/css'>");
tags.push("body { background: "+getClassStyleProperty("column", "background-color")+" }"); // set background color tags.push("body { background: "+getClassStyleProperty("column", "background-color")+" }"); // set background color
tags.push("a[data-full-url] { word-break: break-all }"); // break long urls tags.push("a[data-full-url] { word-break: break-all }"); // break long urls
tags.push(".txt-base-smallest .badge-verified:before { width: 13px !important; height: 13px !important; background-position: -223px -98px !important; }"); // fix cut off badge icon tags.push(".media-item, .media-preview { border-radius: 1px !important }"); // square-ify media
tags.push(".quoted-tweet { border-radius: 0 !important }"); // square-ify quoted tweets
tags.push(".activity-header { align-items: center !important; margin-bottom: 4px }"); // tweak alignment of avatar and text in notifications tags.push(".activity-header { align-items: center !important; margin-bottom: 4px }"); // tweak alignment of avatar and text in notifications
tags.push(".activity-header .tweet-timestamp { line-height: unset }"); // fix timestamp position in notifications tags.push(".activity-header .tweet-timestamp { line-height: unset }"); // fix timestamp position in notifications
if (fontSizeName === "smallest"){
tags.push(".badge-verified:before { width: 13px !important; height: 13px !important; background-position: -223px -98px !important }"); // fix cut off badge icon
}
tags.push("</style>"); tags.push("</style>");
return tags.join(""); doc.setAttribute("data-td-font", fontSizeName);
doc.setAttribute("data-td-theme", themeName);
$TD.loadNotificationLayout(fontSizeName, tags.join(""));
}; };
//
// Block: Hook into settings object to detect when the settings change.
//
TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize, function(name){ TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize, function(name){
$TD.loadFontSizeClass(name); setTimeout(refreshSettings, 0);
}); });
TD.settings.setTheme = appendToFunction(TD.settings.setTheme, function(name){ TD.settings.setTheme = appendToFunction(TD.settings.setTheme, function(name){
document.documentElement.setAttribute("data-td-theme", name); setTimeout(refreshSettings, 0);
setTimeout(function(){
$TD.loadNotificationHeadContents(getNotificationHeadContents());
}, 0);
}); });
onAppReady.push(function(){ onAppReady.push(refreshSettings);
document.documentElement.setAttribute("data-td-theme", TD.settings.getTheme()); })();
$TD.loadFontSizeClass(TD.settings.getFontSize()); //
$TD.loadNotificationHeadContents(getNotificationHeadContents()); // Block: Fix OS name.
}); //
if (ensurePropertyExists(TD, "util", "getOSName")){
TD.util.getOSName = function(){
return "windows";
};
doc.classList.remove("os-");
doc.classList.add("os-windows");
}
// //
// Block: Enable popup notifications. // Block: Enable popup notifications.
@@ -328,9 +348,8 @@
let menu = $(".js-dropdown-content").children("ul").first(); let menu = $(".js-dropdown-content").children("ul").first();
return if menu.length === 0; return if menu.length === 0;
menu.children(".drp-h-divider").last().before('<li class="is-selectable" data-std><a href="#" data-action="tweetduck">TweetDuck</a></li>'); let button = $('<li class="is-selectable" data-tweetduck><a href="#" data-action>TweetDuck</a></li>');
button.insertBefore(menu.children(".drp-h-divider").last());
let button = menu.children("[data-std]");
button.on("click", "a", function(){ button.on("click", "a", function(){
$TD.openContextMenu(); $TD.openContextMenu();
@@ -349,10 +368,6 @@
// Block: Expand shortened links on hover or display tooltip. // Block: Expand shortened links on hover or display tooltip.
// //
(function(){ (function(){
var cutStart = function(str, search){
return str.startsWith(search) ? str.substr(search.length) : str;
};
var prevMouseX = -1, prevMouseY = -1; var prevMouseX = -1, prevMouseY = -1;
var tooltipTimer, tooltipDisplayed; var tooltipTimer, tooltipDisplayed;
@@ -364,13 +379,8 @@
if ($TDX.expandLinksOnHover){ if ($TDX.expandLinksOnHover){
tooltipTimer = window.setTimeout(function(){ tooltipTimer = window.setTimeout(function(){
let expanded = me.attr("data-full-url");
expanded = cutStart(expanded, "https://");
expanded = cutStart(expanded, "http://");
expanded = cutStart(expanded, "www.");
me.attr("td-prev-text", text); me.attr("td-prev-text", text);
me.text(expanded); me.text(me.attr("data-full-url").replace(/^https?:\/\/(www\.)?/, ""));
}, 200); }, 200);
} }
else{ else{
@@ -409,9 +419,11 @@
// //
// Block: Bypass t.co when clicking links and media. // Block: Bypass t.co when clicking links and media.
// //
$(document.body).delegate("a[data-full-url]", "click", function(e){ $(document.body).delegate("a[data-full-url]", "click auxclick", function(e){
if (e.button === 0 || e.button === 1){ // event.which seems to be borked in auxclick
$TD.openBrowser($(this).attr("data-full-url")); $TD.openBrowser($(this).attr("data-full-url"));
e.preventDefault(); e.preventDefault();
}
}); });
if (ensurePropertyExists(TD, "services", "TwitterUser", "prototype", "fromJSONObject")){ if (ensurePropertyExists(TD, "services", "TwitterUser", "prototype", "fromJSONObject")){
@@ -501,8 +513,8 @@
} }
}; };
var processMedia = function(media){ var processMedia = function(chirp){
return media.filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";"); return chirp.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";");
}; };
app.delegate("section.js-column", { app.delegate("section.js-column", {
@@ -529,7 +541,7 @@
let tweetUrl = tweet.getChirpURL(); let tweetUrl = tweet.getChirpURL();
let quoteUrl = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : ""; let quoteUrl = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : "";
let authors = tweet.quotedTweet ? [ tweet.getMainUser().screenName, tweet.quotedTweet.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName; let authors = tweet.quotedTweet ? [ tweet.getMainUser().screenName, tweet.quotedTweet.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName;
let imageList = tweet.quotedTweet ? processMedia(tweet.quotedTweet.getMedia()) : tweet.hasImage() ? processMedia(tweet.getMedia()) : ""; let imageList = tweet.quotedTweet && tweet.quotedTweet.hasImage() ? processMedia(tweet.quotedTweet) : tweet.hasImage() ? processMedia(tweet) : "";
updateHighlightedTweet(me, tweet, tweetUrl || "", quoteUrl || "", authors, imageList); updateHighlightedTweet(me, tweet, tweetUrl || "", quoteUrl || "", authors, imageList);
} }
@@ -586,7 +598,7 @@
setImportantProperty(selectedTweet.find(".js-media-preview-container"), "margin-bottom", "4px"); setImportantProperty(selectedTweet.find(".js-media-preview-container"), "margin-bottom", "4px");
selectedTweet.find(".js-translate-call-to-action").first().remove(); selectedTweet.find(".js-translate-call-to-action").first().remove();
selectedTweet.find(".js-tweet").first().nextAll().remove(); selectedTweet.find(".js-tweet").first().nextAll().remove();
selectedTweet.find("footer").last().prevUntil(":not(.txt-mute.txt-small)").addBack().remove(); // footer, date, location selectedTweet.find("footer").last().prevUntil(":not(.txt-mute)").addBack().remove(); // footer, date, location
} }
else{ else{
setImportantProperty(selectedTweet.find(".js-media-preview-container"), "margin-bottom", "10px"); setImportantProperty(selectedTweet.find(".js-media-preview-container"), "margin-bottom", "10px");
@@ -595,6 +607,7 @@
if (isReply){ if (isReply){
selectedTweet.find(".is-conversation").removeClass("is-conversation"); selectedTweet.find(".is-conversation").removeClass("is-conversation");
selectedTweet.find(".thread").remove();
} }
selectedTweet.find(".js-poll-link").remove(); selectedTweet.find(".js-poll-link").remove();
@@ -681,6 +694,46 @@
}; };
})(); })();
//
// Block: Allow drag & drop behavior for dropping links on columns to open their detail view.
//
(function(){
let tweetRegex = /^https?:\/\/twitter\.com\/[A-Za-z0-9_]+\/status\/(\d+)\/?$/;
let isDraggingValid = false;
window.TDGF_onGlobalDragStart = function(type, data){
isDraggingValid = (type === "link" || type === "text") && tweetRegex.test(data);
};
app.delegate("section.js-column", {
dragover: function(e){
e.originalEvent.dataTransfer.dropEffect = isDraggingValid ? "move" : "none";
e.preventDefault();
e.stopPropagation();
},
drop: function(e){
let match = tweetRegex.exec(e.originalEvent.dataTransfer.getData("URL"));
if (match.length === 2){
let column = TD.controller.columnManager.get($(this).attr("data-column"));
if (column){
TD.controller.clients.getPreferredClient().show(match[1], function(chirp){
TD.ui.updates.showDetailView(column, chirp, column.findChirp(chirp) || chirp);
$(document).trigger("uiGridClearSelection");
}, function(){
alert("error|Could not retrieve the requested tweet.");
});
}
}
e.preventDefault();
e.stopPropagation();
}
});
})();
// //
// Block: Fix scheduled tweets not showing up sometimes. // Block: Fix scheduled tweets not showing up sometimes.
// //
@@ -761,11 +814,13 @@
}); });
// //
// Block: Make middle click on tweet reply icon open the compose drawer. // Block: Make middle click on tweet reply icon open the compose drawer. Only works for non-temporary columns.
// //
app.delegate(".js-reply-action", "mousedown", function(e){ app.delegate(".js-reply-action", "auxclick", function(e){
if (e.which === 2){ if (e.which === 2){
if ($("[data-drawer='compose']").hasClass("is-hidden")){ let column = $(this).closest(".js-column");
if (column && column.hasClass("column") && $("[data-drawer='compose']").hasClass("is-hidden")){
$(document).trigger("uiDrawerShowDrawer", { $(document).trigger("uiDrawerShowDrawer", {
drawer: "compose", drawer: "compose",
withAnimation: true withAnimation: true
@@ -791,74 +846,24 @@
// Block: Inject custom CSS and layout into the page. // Block: Inject custom CSS and layout into the page.
// //
(function(){ (function(){
let styleOfficial = document.createElement("style"); var createStyle = function(id, styles){
document.head.appendChild(styleOfficial); let ele = document.createElement("style");
ele.id = id;
let addRule = (rule) => { ele.innerText = styles;
styleOfficial.sheet.insertRule(rule, 0); document.head.appendChild(ele);
}; };
addRule("a[data-full-url] { word-break: break-all; }"); // break long urls window.TDGF_injectBrowserCSS = function(styles){
addRule(".keyboard-shortcut-list { vertical-align: top; }"); // fix keyboard navigation alignment if (!document.getElementById("tweetduck-browser-css")){
addRule(".account-inline .username { vertical-align: 10%; }"); // move usernames a bit higher createStyle("tweetduck-browser-css", styles);
addRule(".character-count-compose { width: 40px !important; }"); // fix strangely wide character count element }
addRule(".is-video a:not([href*='youtu']) .icon-bg-dot, .is-gif .icon-bg-dot { color: #9f51cf; }"); // change play icon on mp4s };
addRule(".column-nav-link .attribution { position: absolute; }"); // fix cut off account names
addRule(".txt-base-smallest .sprite-verified-mini { width: 13px !important; height: 13px !important; background-position: -223px -99px !important; }"); // fix cut off badge icon when zoomed in
addRule(".txt-base-smallest .badge-verified:before { width: 13px !important; height: 13px !important; background-position: -223px -98px !important; }"); // fix cut off badge icon in notifications
addRule(".btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .media-item, .media-preview, .tooltip-inner { border-radius: 1px !important; }"); // square-ify buttons, dialogs, menus, media previews
addRule(".compose-text-container, .dropdown-menu, .list-item-last, .quoted-tweet, .input-group-button, input, textarea, select { border-radius: 0 !important; }"); // square-ify dropdowns, inputs, quoted tweets, and account selectors
addRule(".prf-header { border-radius: 0; }"); // fix user account header border
addRule(".accs li, .accs img { border-radius: 0 !important; }"); // square-ify retweet account selector
addRule(".accs-header { padding-left: 0 !important; }"); // fix retweet account selector heading
addRule(".scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb, .antiscroll-scrollbar { border-radius: 0; }"); // square-ify scroll bars
addRule(".antiscroll-scrollbar-vertical { margin-top: 0; }"); // square-ify scroll bars
addRule(".antiscroll-scrollbar-horizontal { margin-left: 0; }"); // square-ify scroll bars
addRule(".scroll-styled-v:not(.antiscroll-inner)::-webkit-scrollbar { width: 8px; }"); // square-ify scroll bars
addRule(".scroll-styled-h:not(.antiscroll-inner)::-webkit-scrollbar { height: 8px; }"); // square-ify scroll bars
addRule(".app-columns-container::-webkit-scrollbar { height: 9px !important; }"); // square-ify scroll bars
addRule(".is-condensed .app-header-inner { padding-top: 10px !important; }"); // add extra padding to menu buttons when condensed
addRule(".is-condensed .btn-compose { padding: 8px !important; }"); // fix compose button icon when condensed
addRule(".app-header:not(.is-condensed) .nav-user-info { padding: 0 5px; }"); // add padding to user info
addRule(".app-title { display: none; }"); // hide TweetDeck logo
addRule(".nav-user-info { bottom: 10px !important; }"); // move user info
addRule(".app-navigator { bottom: 50px !important; }"); // move navigation
addRule(".column-navigator-overflow { bottom: 192px !important; }"); // move column list
addRule(".app-navigator .tooltip { display: none !important; }"); // hide broken tooltips in the menu
addRule(".column .column-header { height: 49px !important; }"); // fix one pixel space below column header
addRule(".column:not(.is-options-open) .column-header { border-bottom: none; }"); // fix one pixel space below column header
addRule(".is-options-open .column-type-icon { bottom: 27px; }"); // fix one pixel space below column header
addRule(".activity-header { align-items: center !important; margin-bottom: 4px; }"); // tweak alignment of avatar and text in notifications
addRule(".activity-header .tweet-timestamp { line-height: unset; }"); // fix timestamp position in notifications
addRule(".account-bio.padding-t--5 { padding-top: 2px !important; }"); // decrease padding on follow notifications
addRule("html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu { color: #000; border-color: #000; opacity: 0.25; }"); // make follow notification button nicer
addRule("html[data-td-theme='dark'] .stream-item:not(:hover) .js-user-actions-menu { color: #fff; border-color: #fff; opacity: 0.25; }"); // make follow notification button nicer
addRule(".app-columns-container::-webkit-scrollbar-track { border-left: 0; }"); // remove weird border in the column container scrollbar
addRule(".app-columns-container { bottom: 0 !important; }"); // move column container scrollbar to bottom to fit updated style
addRule(".js-column-header .column-header-link { padding: 0; }"); // fix column header tooltip hover box
addRule(".js-column-header .column-header-link .icon { padding: 9px 4px; width: calc(1em + 8px); height: 100%; box-sizing: border-box; }"); // fix column header tooltip hover box
addRule("#td-compose-drawer-pin { margin: 17px 4px 0 0; transition: transform 0.1s ease; fill: #fff; float: right; cursor: pointer; }"); // replace 'stay open' checkbox with a pin icon
addRule(".js-docked-compose footer { display: none; }"); // replace 'stay open' checkbox with a pin icon
addRule(".compose-content { bottom: 0 !important; }"); // replace 'stay open' checkbox with a pin icon
addRule(".js-docked-compose .js-drawer-close { margin: 20px 0 0 !important; }"); // fix close drawer button because twitter is fucking incompetent
window.TDGF_reinjectCustomCSS = function(styles){ window.TDGF_reinjectCustomCSS = function(styles){
$("#tweetduck-custom-css").remove(); $("#tweetduck-custom-css").remove();
if (styles && styles.length){ if (styles && styles.length){
$(document.head).append("<style type='text/css' id='tweetduck-custom-css'>"+styles+"</style>"); createStyle("tweetduck-custom-css", styles);
} }
}; };
})(); })();
@@ -893,19 +898,38 @@
$TD.playVideo(url); $TD.playVideo(url);
}; };
app.delegate(".js-gif-play", "click", function(e){ var getVideoTweetLink = function(obj){
let parent = obj.closest(".js-tweet").first();
let link = (parent.hasClass("tweet-detail") ? parent.find("a[rel='url']") : parent.find("time").first().children("a")).first();
return link.attr("href");
}
app.delegate(".js-gif-play", {
click: function(e){
let src = !e.ctrlKey && $(this).closest(".js-media-gif-container").find("video").attr("src"); let src = !e.ctrlKey && $(this).closest(".js-media-gif-container").find("video").attr("src");
if (src){ if (src){
playVideo(src); playVideo(src);
} }
else{ else{
let parent = $(e.target).closest(".js-tweet").first(); $TD.openBrowser(getVideoTweetLink($(this)));
let link = (parent.hasClass("tweet-detail") ? parent.find("a[rel='url']") : parent.find("time").first().children("a")).first();
$TD.openBrowser(link.attr("href"));
} }
e.stopPropagation(); e.stopPropagation();
},
mousedown: function(e){
if (e.button === 1){
e.preventDefault();
}
},
mouseup: function(e){
if (e.button === 1){
$TD.openBrowser(getVideoTweetLink($(this)));
e.preventDefault();
}
}
}); });
TD.mustaches["status/media_thumb.mustache"] = TD.mustaches["status/media_thumb.mustache"].replace("is-gif", "is-gif is-paused"); TD.mustaches["status/media_thumb.mustache"] = TD.mustaches["status/media_thumb.mustache"].replace("is-gif", "is-gif is-paused");
@@ -980,6 +1004,28 @@
} }
}); });
//
// Block: Make temporary search column appear as the first one and clear the input box.
//
$(document).on("uiSearchNoTemporaryColumn", function(e, data){
if (data.query && data.searchScope !== "users" && !data.columnKey){
if ($TDX.openSearchInFirstColumn){
let order = TD.controller.columnManager._columnOrder;
if (order.length > 1){
let columnKey = order[order.length-1];
order.splice(order.length-1, 1);
order.splice(1, 0, columnKey);
TD.controller.columnManager.move(columnKey, "left");
}
}
$(".js-app-search-input").val("");
$(".js-perform-search").blur();
}
});
// //
// Block: Fix DM reply input box not getting focused after opening a conversation. // Block: Fix DM reply input box not getting focused after opening a conversation.
// //
@@ -1056,31 +1102,34 @@
}); });
}; };
(function(){
var lastActivity = Date.now();
$(document).click(function(e){
lastActivity = Date.now();
});
window.TDGF_tryRunCleanup = function(){ window.TDGF_tryRunCleanup = function(){
// no recent activity
return false if Date.now()-lastActivity < 15e3;
// no modals are visible // no modals are visible
return false if $("#open-modal").is(":visible") || !$(".js-modals-container").is(":empty"); return false if $(".js-modal").is(":visible") || !$(".js-modals-container").is(":empty");
// all columns are in a default state // all columns are in a default state
return false if $("section.js-column").is(".is-shifted-1,.is-shifted-2"); return false if $("section.js-column").is(".is-shifted-1,.is-shifted-2");
// all textareas are empty // all textareas are empty
if ($("textarea").is(function(){ return false if Array.prototype.some.call(document.getElementsByTagName("textarea"), ele => ele.value.length > 0);
return $(this).val().length > 0;
})){
return false;
}
// all columns are scrolled to top // all columns are scrolled to top
if ($(".js-column-scroller").is(function(){ return false if Array.prototype.some.call(document.getElementsByClassName("js-column-scroller"), ele => ele.scrollTop > 0);
return $(this).scrollTop() > 0;
})){
return false;
}
// cleanup // cleanup
window.TDGF_reload(); window.TDGF_reload();
return true; return true;
}; };
})();
if (window.TD_SESSION && window.TD_SESSION.gc){ if (window.TD_SESSION && window.TD_SESSION.gc){
var state; var state;

View File

@@ -0,0 +1,97 @@
(function($, $TD){
$(document).one("TD.ready", function(){
let css = $(`
<style>
#td-introduction-modal {
display: block;
}
#td-introduction-modal .mdl {
width: 90%;
max-width: 800px;
height: 372px;
}
#td-introduction-modal .mdl-header-title {
cursor: default;
}
#td-introduction-modal .mdl-content {
padding: 4px 16px 0;
overflow-y: auto;
}
#td-introduction-modal p {
margin: 12px 0;
font-size: 1.4rem;
}
#td-introduction-modal p strong {
font-weight: normal;
text-shadow: 0 0 #000;
}
#td-introduction-modal footer {
padding: 10px 0;
}
#td-introduction-modal button {
margin-left: 8px;
}
#td-introduction-modal .anondata {
float: left;
margin: 5px 7px;
}
#td-introduction-modal .anondata input {
vertical-align: -10%;
}
#td-introduction-modal .anondata label {
cursor: pointer;
display: inline-block;
font-size: 14px;
}
</style>`).appendTo(document.head);
let ele = $(`
<div id="td-introduction-modal" class="ovl">
<div class="mdl is-inverted-dark">
<header class="mdl-header">
<h3 class="mdl-header-title">Welcome to TweetDuck</h3>
<a href="#" class="mdl-dismiss link-normal-dark"><i class="icon icon-close"></i></a>
</header>
<div class="mdl-inner">
<div class="mdl-content">
<p>Thank you for downloading TweetDuck!</p>
<p><strong>Right-click anywhere</strong> or click <strong>Settings TweetDuck</strong> in the left panel to open the main menu, where you can access the <strong>Options</strong>, <strong>Plugins</strong>, and more.</p>
<p>You can also right-click links, media, tweets, notifications, etc. to access their context menu.</p>
<p>If you're using TweetDuck for the first time, check out the <strong>guide</strong> that answers common questions and showcases important features. You can open the main menu, select <strong>About TweetDuck</strong>, and click the help button to view the guide later.</p>
<p>Before you continue, please consider helping development by allowing TweetDuck to send anonymous data in the future. You can always disable it in <strong>Options Feedback</strong>.</p>
</div>
<footer class="txt-right">
<div class="anondata">
<input id="td-anonymous-data" type="checkbox" checked>
<label for="td-anonymous-data">Send anonymous usage data</label>
<label>&nbsp;(<a href="https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data" rel="nofollow">learn more</a>)</label>
</div>
<button class="btn btn-positive" data-guide><span class="label">Show Guide</span></button>
<button class="btn btn-positive"><span class="label">Close</span</button>
</footer>
</div>
</div>
</div>`).appendTo(".js-app");
ele.find("button, a.mdl-dismiss").click(function(){
let showGuide = $(this)[0].hasAttribute("data-guide");
let allowDataCollection = $("#td-anonymous-data").is(":checked");
ele.fadeOut(200, function(){
$TD.onIntroductionClosed(showGuide, allowDataCollection);
ele.remove();
css.remove();
});
});
});
})($, $TD);

View File

@@ -42,10 +42,6 @@
// Block: Expand shortened links on hover or display tooltip. // Block: Expand shortened links on hover or display tooltip.
// //
(function(){ (function(){
var cutStart = function(str, search){
return str.startsWith(search) ? str.substr(search.length) : str;
};
var prevMouseX = -1, prevMouseY = -1; var prevMouseX = -1, prevMouseY = -1;
var tooltipTimer, tooltipDisplayed; var tooltipTimer, tooltipDisplayed;
@@ -60,13 +56,8 @@
if ($TDX.expandLinksOnHover){ if ($TDX.expandLinksOnHover){
tooltipTimer = window.setTimeout(function(){ tooltipTimer = window.setTimeout(function(){
var expanded = url;
expanded = cutStart(expanded, "https://");
expanded = cutStart(expanded, "http://");
expanded = cutStart(expanded, "www.");
me.setAttribute("td-prev-text", text); me.setAttribute("td-prev-text", text);
me.innerHTML = expanded; me.innerHTML = url.replace(/^https?:\/\/(www\.)?/, "");
}, 200); }, 200);
} }
else{ else{
@@ -112,11 +103,11 @@
// Block: Setup a skip button. // Block: Setup a skip button.
// //
if (!document.body.hasAttribute("td-example-notification")){ if (!document.body.hasAttribute("td-example-notification")){
document.body.insertAdjacentHTML("afterbegin", [ document.body.insertAdjacentHTML("afterbegin", `
'<svg id="td-skip" xmlns="http://www.w3.org/2000/svg" width="10" height="17" viewBox="0 0 350 600" style="position:fixed;left:30px;bottom:10px;z-index:1000">', <svg id="td-skip" width="10" height="17" viewBox="0 0 350 600">
'<path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">', <path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">
'</svg>' </svg>
].join("")); `);
document.getElementById("td-skip").addEventListener("click", function(){ document.getElementById("td-skip").addEventListener("click", function(){
$TD.loadNextNotification(); $TD.loadNextNotification();

View File

@@ -3,7 +3,7 @@
<div class="js-tweet tweet"> <div class="js-tweet tweet">
<header class="tweet-header"> <header class="tweet-header">
<time class="tweet-timestamp js-timestamp pull-right txt-mute"> <time class="tweet-timestamp js-timestamp pull-right txt-mute">
<a target="_blank" rel="url" href="https://twitter.com/chylexmc" class="txt-small">0s</a> <a target="_blank" rel="url" href="https://twitter.com/chylexmc" class="txt-size-variable--12">0s</a>
</time> </time>
<a target="_blank" rel="user" href="https://twitter.com/chylexmc" class="account-link link-complex block"> <a target="_blank" rel="user" href="https://twitter.com/chylexmc" class="account-link link-complex block">
<div class="obj-left item-img tweet-img"> <div class="obj-left item-img tweet-img">

View File

@@ -0,0 +1,263 @@
/***********************/
/* Redesign scrollbars */
/***********************/
.scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb, .antiscroll-scrollbar {
border-radius: 0;
}
.antiscroll-scrollbar-vertical {
margin-top: 0;
}
.antiscroll-scrollbar-horizontal {
margin-left: 0;
}
.scroll-styled-v:not(.antiscroll-inner)::-webkit-scrollbar {
width: 8px;
}
.scroll-styled-h:not(.antiscroll-inner)::-webkit-scrollbar {
height: 8px;
}
.app-columns-container::-webkit-scrollbar {
height: 9px !important;
}
.app-columns-container::-webkit-scrollbar-track {
border-left: 0;
}
.app-columns-container {
bottom: 0 !important;
}
/********************/
/* Square-ify stuff */
/********************/
.btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .tooltip-inner {
border-radius: 1px !important;
}
.media-item, .media-preview, .media-image, .js-media-added .br--4 {
border-radius: 1px !important;
}
.compose-text-container, .compose-reply-tweet, .compose-message-recipient-input-container, .compose-message-recipient, .compose-media-bar-holder, .media-grid-container {
border-radius: 0 !important;
}
.dropdown-menu, .list-item-last, .quoted-tweet, .input-group-button, input, textarea, select, .prf-header, .accs li, .accs img {
border-radius: 0 !important;
}
/***********************/
/* Hide TweetDeck logo */
/***********************/
.app-title {
display: none;
}
.nav-user-info {
bottom: 10px !important;
}
.app-navigator {
bottom: 50px !important;
}
.column-navigator-overflow {
bottom: 192px !important;
}
/*************************************/
/* Tweak collapsed left panel layout */
/*************************************/
.is-condensed .app-header-inner {
padding-top: 10px !important;
}
.is-condensed .btn-compose {
padding: 8px !important;
}
.app-header:not(.is-condensed) .nav-user-info {
padding: 0 5px;
}
/****************************************/
/* Tweak notification layout and design */
/****************************************/
.activity-header {
align-items: center !important;
margin-bottom: 4px;
}
.activity-header .tweet-timestamp {
line-height: unset;
}
.account-bio.padding-t--5 {
/* follow notification padding */
padding-top: 2px !important;
}
html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu {
color: #000;
border-color: #000;
opacity: 0.25;
}
html[data-td-theme='dark'] .stream-item:not(:hover) .js-user-actions-menu {
color: #fff;
border-color: #fff;
opacity: 0.25;
}
/***********************/
/* Tweaks for features */
/***********************/
a[data-full-url] {
/* break long urls */
word-break: break-all;
}
.character-count-compose {
/* fix strangely wide character count element */
width: 40px !important;
}
.is-video a:not([href*='youtu']) .icon-bg-dot, .is-gif .icon-bg-dot {
/* change play icon on mp4s */
color: #9f51cf;
}
/***************************************/
/* Replace 'Stay open' with a pin icon */
/***************************************/
#td-compose-drawer-pin {
margin: 17px 4px 0 0;
float: right;
fill: #fff;
cursor: pointer;
transition: transform 0.1s ease;
}
.js-docked-compose footer {
display: none;
}
.compose-content {
bottom: 0 !important;
}
/*******************************************/
/* Fix general visual issues or annoyances */
/*******************************************/
.account-inline .username {
/* move usernames a bit higher */
vertical-align: 10%;
}
html[data-td-font='smallest'] .sprite-verified-mini {
/* fix cut off badge when zoomed in */
width: 13px !important;
height: 13px !important;
background-position: -223px -99px !important;
}
html[data-td-font='smallest'] .badge-verified:before {
/* fix cut off badge in notifications */
width: 13px !important;
height: 13px !important;
background-position: -223px -98px !important;
}
.accs-header {
/* fix retweet account selector heading */
padding-left: 0 !important;
}
.keyboard-shortcut-list {
/* fix keyboard navigation alignment */
vertical-align: top;
}
.tweet-detail-wrapper .js-media-gif-container {
/* GIFs in detail view don't trigger the pointer cursor */
cursor: pointer;
}
.is-inverted-dark .inline-reply .btn:hover {
/* Reply buttons in modals are bork */
background-color: transparent;
}
/***************************************************************/
/* Fix glaring visual issues that twitter hasn't fixed yet smh */
/***************************************************************/
.column-nav-link .attribution {
/* fix cut off account names */
position: absolute;
}
.js-docked-compose .js-drawer-close {
/* fix close drawer button position */
margin: 20px 0 0 !important;
}
.compose-reply-tweet-remove {
/* fix close reply button position; this would make sense if the reply account name became full size, but it still cuts off where the button used to be */
top: 3px !important;
right: -2px !important;
}
#account-safeguard-checkbox, #inline-account-safeguard-checkbox {
/* fix "Ready to Tweet/send" alignment with the checkbox */
vertical-align: -15%;
}
/************************************************/
/* Fix tooltips in navigation and column header */
/************************************************/
.app-navigator .tooltip {
display: none !important;
}
.js-column-header .column-header-link {
padding: 0;
}
.js-column-header .column-header-link .icon {
width: calc(1em + 8px);
height: 100%;
padding: 9px 4px;
box-sizing: border-box;
}
/*******************************************/
/* Fix one pixel space below column header */
/*******************************************/
.column .column-header {
height: 49px !important;
}
.column:not(.is-options-open) .column-header {
border-bottom: none;
}
.is-options-open .column-type-icon {
bottom: 27px;
}

View File

@@ -0,0 +1,57 @@
/* General */
body:before {
content: none;
}
body {
overflow-y: auto;
}
.scroll-styled-v::-webkit-scrollbar {
width: 7px;
}
.scroll-styled-v::-webkit-scrollbar-thumb {
border-radius: 0;
}
.scroll-styled-v::-webkit-scrollbar-track {
border-left: 0;
}
/* Media */
.media-size-medium {
max-height: 240px;
height: calc(100vh - 16px) !important;
border-radius: 1px !important;
}
.js-quote-detail .media-size-medium {
height: calc(100vh - 28px) !important;
}
.js-media.margin-vm, .js-media-preview-container.margin-vm {
margin-bottom: 0 !important;
}
/* Skip button */
#td-skip {
position: fixed;
left: 30px;
bottom: 10px;
z-index: 1000;
cursor: pointer;
opacity: 0;
transition: opacity 0.15s ease;
}
.td-hover #td-skip {
opacity: 0.75;
}
#td-skip:hover {
opacity: 1;
}

View File

@@ -88,6 +88,8 @@
<SubType>Component</SubType> <SubType>Component</SubType>
</Compile> </Compile>
<Compile Include="Core\FormManager.cs" /> <Compile Include="Core\FormManager.cs" />
<Compile Include="Core\Handling\ContextMenuGuide.cs" />
<Compile Include="Core\Handling\DragHandlerBrowser.cs" />
<Compile Include="Core\Handling\General\BrowserProcessHandler.cs" /> <Compile Include="Core\Handling\General\BrowserProcessHandler.cs" />
<Compile Include="Core\Handling\ContextMenuBase.cs" /> <Compile Include="Core\Handling\ContextMenuBase.cs" />
<Compile Include="Core\Handling\ContextMenuBrowser.cs" /> <Compile Include="Core\Handling\ContextMenuBrowser.cs" />
@@ -132,6 +134,12 @@
<Compile Include="Core\Other\FormAbout.Designer.cs"> <Compile Include="Core\Other\FormAbout.Designer.cs">
<DependentUpon>FormAbout.cs</DependentUpon> <DependentUpon>FormAbout.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Other\FormGuide.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Core\Other\FormGuide.Designer.cs">
<DependentUpon>FormGuide.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\FormMessage.cs"> <Compile Include="Core\Other\FormMessage.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -170,6 +178,18 @@
<DependentUpon>DialogSettingsRestart.cs</DependentUpon> <DependentUpon>DialogSettingsRestart.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Other\Management\BrowserProcesses.cs" /> <Compile Include="Core\Other\Management\BrowserProcesses.cs" />
<Compile Include="Core\Other\Settings\TabSettingsFeedback.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Core\Other\Settings\TabSettingsFeedback.Designer.cs">
<DependentUpon>TabSettingsFeedback.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\Settings\TabSettingsTray.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Core\Other\Settings\TabSettingsTray.Designer.cs">
<DependentUpon>TabSettingsTray.cs</DependentUpon>
</Compile>
<Compile Include="Core\Utils\StringUtils.cs" /> <Compile Include="Core\Utils\StringUtils.cs" />
<Compile Include="Core\Utils\TwitterUtils.cs" /> <Compile Include="Core\Utils\TwitterUtils.cs" />
<Compile Include="Data\CombinedFileStream.cs" /> <Compile Include="Data\CombinedFileStream.cs" />
@@ -326,12 +346,15 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Resources\Scripts\code.js" /> <Content Include="Resources\Scripts\code.js" />
<Content Include="Resources\Scripts\introduction.js" />
<Content Include="Resources\Scripts\notification.js" /> <Content Include="Resources\Scripts\notification.js" />
<Content Include="Resources\Scripts\pages\error.html" /> <Content Include="Resources\Scripts\pages\error.html" />
<Content Include="Resources\Scripts\pages\example.html" /> <Content Include="Resources\Scripts\pages\example.html" />
<Content Include="Resources\Scripts\plugins.browser.js" /> <Content Include="Resources\Scripts\plugins.browser.js" />
<Content Include="Resources\Scripts\plugins.js" /> <Content Include="Resources\Scripts\plugins.js" />
<Content Include="Resources\Scripts\plugins.notification.js" /> <Content Include="Resources\Scripts\plugins.notification.js" />
<Content Include="Resources\Scripts\styles\browser.css" />
<Content Include="Resources\Scripts\styles\notification.css" />
<Content Include="Resources\Scripts\twitter.js" /> <Content Include="Resources\Scripts\twitter.js" />
<Content Include="Resources\Scripts\update.js" /> <Content Include="Resources\Scripts\update.js" />
</ItemGroup> </ItemGroup>

View File

@@ -5,7 +5,7 @@ using System.Windows.Forms;
namespace TweetDuck.Video{ namespace TweetDuck.Video{
static class Program{ static class Program{
internal const string Version = "1.2.0.0"; internal const string Version = "1.2.1.0";
// referenced in VideoPlayer // referenced in VideoPlayer
// set by task manager -- public const int CODE_PROCESS_KILLED = 1; // set by task manager -- public const int CODE_PROCESS_KILLED = 1;