2024了,有哪些好用的软件或功能是其他系统还没有的?

大家有没有遇到过只在某特定系统上独有,但好用的软件或系统内置功能

讨论范围

  • 主要是桌面端与桌面端、移动端与移动端对比
  • 不仅限于独占,比如 Linux 和 Mac 有,但 Windows 没有也算
  • 同系统不同版本也算,比如 Win7 有,但 Win11 没有
  • 也许有但可能比较小众没有被发现也算
  • 也许有"平替",但"平替"过于简陋或体验太差也算
  • 交互操作也算
  • 不考虑:游戏、杀毒、虚拟机

发帖目的

  • 发掘好用的软件或功能
  • 为体验新系统做准备

无意比较各系统的优劣,因为比较主观所以请大家友善讨论

说说个人体验,一直用惯了 Windows,因为其他原因用过一段时间的 Mac,我个人主观觉得窗口管理功能这一块 Windows 真是做的比 Mac 好,Windows 各种软件窗口切换就很流畅,Mac 我觉得太乱了,随时找不到想要的窗口在哪,还有全屏化切来切去全屏一多就不好记要切到哪。Dock 栏两边空一截觉得难受特别是窗口放大之后(非全屏)整个屏幕下面留个 Dock 还空两边太扎眼了。好看归好看但用着不如 Windows 方便。

上面仅是我个人用过半年多 Mac 主观感受,无意引战,要是有人觉得我说的不对没掌握到 Mac 窗口管理的精髓可以留下评论,欢迎赐教。

赞同,补充一条,MacOS 的文件管理器访达难用无比,犹如行舟无桨,却没有人开发出可与 Windows 上 Directory Opus、Totalcmd 的一半并肩的文件管理增强工具

Linux 有各种桌面环境和窗口管理器。

这里有很多漂亮的桌面。

https://www.reddit.com/r/unixporn/

Windows的兼容性,真的是没比的。
特别是以前的微软。
作为开发者和使用者,是真的喜欢。

不仅Windows,以前的Windows Mobile(不是wp、w10m) 的兼容性同样也是如此。ppc2002上的app在wm6.5.3上照样完美运行。

后来随 .net framework的发布,wm端专用的 .Net
Compact framework 也同步发布。这样使用 .net cf开发的移动端app可以直接在电脑端运行,而不需要模拟器,是真的方便。

不过后来的wp直接不兼容wm,可以说是wp灭亡的祸根。

linux下没有everything :frowning_face: :frowning_face: :frowning_face:

可以看看同类软件。

https://alternativeto.net/software/everything/?platform=linux

Linux 桌面各个窗口管理器一般有个功能是按住 alt 然后就可以按左键拖动窗口,右键修改窗口大小。

这个功能我太习惯了,以至于让我改用 Windows 非常难以适应。

不过这个功能好像 GNOME 没有。

这个autohotkey脚本适合你

#Persistent
#SingleInstance, Force

; Hotkeys
;	1. Alt + LButton = Move window
;	2. Alt + Shift + LButton = Move window along X axis only
;	3. Alt + Ctrl + LButton = Move window along Y axis only
;	4. Alt + RButton = Resize
;	5. Alt + Shift + RButton = Resize window width only
;	6. Alt + Ctrl + RButton = Resize window height only
;	7. Alt + MButton OR Alt + Win + C = Change text within window
;	8. Win + LButton = Enable/Disable Window

Menu, TRAY, NoStandard
Menu, TRAY, MainWindow ; For compiled scripts
Menu, TRAY, Add, &Reload, Reload
Menu, TRAY, Add, E&xit, Exit

CoordMode, Mouse
SetWinDelay, -1

g_iIntraMonitorThreashold := 350 ; pixels

return ; End auto-execute

Reload:
	Reload
Exit:
	ExitApp

#!c::
{
	gosub ChangeWindowTitle
	return
}

; Simple hotkeys like ~Alt & ~LButton cannot be used becuase this does not disable clicks inside of windows
Shift & ~Alt::
Ctrl & ~Alt::
~Alt::
{
	Hotkey, *LButton, Alt_And_LButton, On
	Hotkey, *RButton, Alt_And_RButton, On
	Hotkey, *MButton, ChangeWindowTitle, On
	KeyWait, Alt
	Hotkey, *LButton, Off
	Hotkey, *RButton, Off
	Hotkey, *MButton, Off

	if (A_ThisHotkey = "*LButton")
		gosub Alt_And_LButton
	else if (A_ThisHotkey = "*RButton")
		gosub Alt_And_RButton
	else if (A_ThisHotkey = "*MButton")
		gosub ChangeWindowTitle

	return
}

Alt_And_LButton:
{
	iPrevMouseX := iPrevMouseY := A_Blank
	MouseGetPos,,, hWnd

	if (IsApprovedHwnd(hWnd))
	{
		g_bUseXThreshold := g_bUseYThreshold := true
		g_iXDeltaAtLeftCorner := g_iXDeltaAtRightCorner := g_iYDeltaAtTopCorner := g_iYDeltaAtBottomCorner := 0

		while (GetKeyState("Alt", "P") && GetKeyState("LButton", "P"))
		{
			bIgnoreX := GetKeyState("Ctrl", "P")
			bIgnoreY := GetKeyState("Shift", "P")

			MouseGetPos, iMouseX, iMouseY
			iMouseX := iMouseX
			iMouseY := iMouseY

			WinGetPos, iX, iY, iWndW, iWndH, ahk_id %hWnd%
			iXDelta := bIgnoreX ? 0 : iMouseX - (iPrevMouseX == A_Blank ? iMouseX : iPrevMouseX)
			iYDelta := bIgnoreY ? 0 : iMouseY - (iPrevMouseY == A_Blank ? iMouseY : iPrevMouseY)

			if (iXDelta == 0 && iYDelta == 0)
			{
				iPrevMouseX := iMouseX
				iPrevMouseY := iMouseY
				continue
			}

			iX := iX + iXDelta
			iY := iY + iYDelta

			bMoveX := bMoveY := true
			rectMonMouseIsOn := GetMonitorRectAt(iMouseX, iMouseY)

			; if we have passed a monitor corner over onto another monitor, or else we have shifted directions towards to opposite corner of the monitor, reset
			if (abs(iMouseX) - abs(iMouseXPosAtCorner) < g_iIntraMonitorThreashold)
				g_bUseXThreshold := true
			if (abs(iMouseY) - abs(iMouseYPosAtCorner) < g_iIntraMonitorThreashold)
				g_bUseYThreshold := true

			if (g_bUseXThreshold)
			{
				if (!bIgnoreX)
				{
					if (iXDelta < 0 ; moving window to the left
					&& iX - iXDelta >= rectMonMouseIsOn.left ; the left corner of the wnd is not already past the monitor's left corner
					&& iX < rectMonMouseIsOn.left) ; we are trying to move the window past the left corner
					{
						g_iXDeltaAtLeftCorner += abs(iXDelta)

						if (g_iXDeltaAtLeftCorner < g_iIntraMonitorThreashold)
						{
							bMoveX := false
							g_bUseXThreshold := true
						}
						else
						{
							bMoveX := true
							g_bUseXThreshold := false

							iMouseXPosAtCorner := iMouseX
							g_iXDeltaAtLeftCorner := 0
						}
					}
					else if (iXDelta > 0 ; moving window to the right
					&& iX - iXDelta + iWndW <= rectMonMouseIsOn.right ; the right corner of the wnd is not already past the monitor's right corner
					&& (iX + iWndW) > rectMonMouseIsOn.right) ; we are trying to move the window past the right corner
					{
						g_iXDeltaAtRightCorner += abs(iXDelta)

						if (g_iXDeltaAtRightCorner < g_iIntraMonitorThreashold)
						{
							bMoveX := false
							g_bUseXThreshold := true
						}
						else
						{
							bMoveX := true
							g_bUseXThreshold := false

							iMouseXPosAtCorner := iMouseX
							g_iXDeltaAtRightCorner := 0
						}
					}
				}
			}
			if (g_bUseYThreshold)
			{
				if (!bIgnoreY)
				{
					if (iYDelta < 0 ; moving window to the bottom
					&& iY - iYDelta >= rectMonMouseIsOn.top ; the top corner of the wnd is not already past the monitor's top corner
					&& iY < rectMonMouseIsOn.top) ; we are trying to move the window past the top corner
					{
						g_iYDeltaAtBottomCorner += abs(iYDelta)

						if (g_iYDeltaAtBottomCorner < g_iIntraMonitorThreashold)
						{
							bMoveY := false
							g_bUseYThreshold := true
						}
						else
						{
							bMoveY := true
							g_bUseYThreshold := false

							iMouseYPosAtCorner := iMouseY
							g_iYDeltaAtBottomCorner := 0
						}
					}
					else if (iYDelta > 0 ; moving window to the right
					&& iY - iYDelta + iWndH <= rectMonMouseIsOn.bottom ; the right corner of the wnd is not already past the monitor's bottom corner
					&& (iY + iWndH) > rectMonMouseIsOn.bottom) ; we are trying to move the window past the bottom corner
					{
						g_iYDeltaAtTopCorner += abs(iYDelta)

						if (g_iYDeltaAtTopCorner < g_iIntraMonitorThreashold)
						{
							bMoveY := false
							g_bUseYThreshold := true
						}
						else
						{
							bMoveY := true
							g_bUseYThreshold := false

							iMouseYPosAtCorner := iMouseY
							g_iYDeltaAtTopCorner := 0
						}
					}
				}
			}

			WinMove, ahk_id %hWnd%,, % bMoveX ? iX : "", bMoveY ? iY : ""

			iPrevMouseX := iMouseX
			iPrevMouseY := iMouseY
		}
	}

	return
}

Alt_And_RButton:
{
	iPrevMouseX := iPrevMouseY := A_Blank
	g_bUseWThreshold := g_bUseHThreshold := true
	MouseGetPos,,, hWnd

	if (IsApprovedHwnd(hWnd))
	{
		while (GetKeyState("Alt", "P") && GetKeyState("RButton", "P"))
		{
			bIgnoreW := GetKeyState("Ctrl", "P")
			bIgnoreH := GetKeyState("Shift", "P")

			MouseGetPos, iMouseX, iMouseY
			iMouseX := iMouseX
			iMouseY := iMouseY

			WinGetPos, iX, iY, iWndW, iWndH, ahk_id %hWnd%
			iXDelta := bIgnoreW ? 0 : iMouseX - (iPrevMouseX == A_Blank ? iMouseX : iPrevMouseX)
			iYDelta := bIgnoreH ? 0 : iMouseY - (iPrevMouseY == A_Blank ? iMouseY : iPrevMouseY)

			if (iXDelta == 0 && iYDelta == 0)
			{
				iPrevMouseX := iMouseX
				iPrevMouseY := iMouseY
				continue
			}

			iWndW := iWndW + iXDelta
			iWndH := iWndH + iYDelta

			bMoveW := bMoveH := true
			rectMonMouseIsOn := GetMonitorRectAt(iMouseX, iMouseY)

			; if we have passed a monitor corner over onto another monitor, or else we have shifted directions towards to opposite corner of the monitor, reset
			if (abs(iMouseX) - abs(iMouseXPosAtCorner) < g_iIntraMonitorThreashold)
				g_bUseWThreshold := true
			if (abs(iMouseY) - abs(iMouseYPosAtCorner) < g_iIntraMonitorThreashold)
				g_bUseHThreshold := true

			if (g_bUseWThreshold)
			{
				if (!bIgnoreW)
				{
					if (iXDelta < 0 ; moving window to the left
					&& iX - iXDelta >= rectMonMouseIsOn.left ; the left corner of the wnd is not already past the monitor's left corner
					&& iX < rectMonMouseIsOn.left) ; we are trying to move the window past the left corner
					{
						g_iXDeltaAtLeftCorner += abs(iXDelta)

						if (g_iXDeltaAtLeftCorner < g_iIntraMonitorThreashold)
						{
							bMoveW := false
							g_bUseWThreshold := true
						}
						else
						{
							bMoveW := true
							g_bUseWThreshold := false

							iMouseXPosAtCorner := iMouseX
							g_iXDeltaAtLeftCorner := 0
						}
					}
					else if (iXDelta > 0 ; moving window to the right
					&& iX - iXDelta + iWndW <= rectMonMouseIsOn.right ; the right corner of the wnd is not already past the monitor's right corner
					&& (iX + iWndW) > rectMonMouseIsOn.right) ; we are trying to move the window past the right corner
					{
						g_iXDeltaAtRightCorner += abs(iXDelta)

						if (g_iXDeltaAtRightCorner < g_iIntraMonitorThreashold)
						{
							bMoveW := false
							g_bUseWThreshold := true
						}
						else
						{
							bMoveW := true
							g_bUseWThreshold := false

							iMouseXPosAtCorner := iMouseX
							g_iXDeltaAtRightCorner := 0
						}
					}
				}
			}
			if (g_bUseHThreshold)
			{
				if (!bIgnoreH)
				{
					if (iYDelta < 0 ; moving window to the bottom
					&& iY - iYDelta >= rectMonMouseIsOn.top ; the top corner of the wnd is not already past the monitor's top corner
					&& iY < rectMonMouseIsOn.top) ; we are trying to move the window past the top corner
					{
						g_iYDeltaAtBottomCorner += abs(iYDelta)

						if (g_iYDeltaAtBottomCorner < g_iIntraMonitorThreashold)
						{
							bMoveH := false
							g_bUseHThreshold := true
						}
						else
						{
							bMoveH := true
							g_bUseHThreshold := false

							iMouseYPosAtCorner := iMouseY
							g_iYDeltaAtBottomCorner := 0
						}
					}
					else if (iYDelta > 0 ; moving window to the right
					&& iY - iYDelta + iWndH <= rectMonMouseIsOn.bottom ; the right corner of the wnd is not already past the monitor's bottom corner
					&& (iY + iWndH) > rectMonMouseIsOn.bottom) ; we are trying to move the window past the bottom corner
					{
						g_iYDeltaAtTopCorner += abs(iYDelta)

						if (g_iYDeltaAtTopCorner < g_iIntraMonitorThreashold)
						{
							bMoveH := false
							g_bUseHThreshold := true
						}
						else
						{
							bMoveH := true
							g_bUseHThreshold := false

							iMouseYPosAtCorner := iMouseY
							g_iYDeltaAtTopCorner := 0
						}
					}
				}
			}

			WinMove, ahk_id %hWnd%,,,, % bMoveW ? iWndW : "", bMoveH ? iWndH : ""

			iPrevMouseX := iMouseX
			iPrevMouseY := iMouseY
		}
	}

	return
}

ChangeWindowTitle:
{
	; Turn off hotkeys so that the LButton is not responise
	Hotkey, *LButton, Off
	Hotkey, *RButton, Off
	Hotkey, *MButton, Off

	; This hotkey seems to be triggered twice every time it is activated, so g_iTimeAtThisExecution is used to prevent double-execution
	g_iTimeAtThisExecution := SubStr(A_Now, StrLen(A_Now) - 3, 4)
	if (A_ThisHotkey = "*MButton" && g_iTimeAtLastExecution != A_Blank && g_iTimeAtThisExecution - g_iTimeAtLastExecution < 1)
		return

	MouseGetPos,,, hWnd

	if (IsApprovedHwnd(hWnd))
	{
		WinGetTitle, sExistingTitle, ahk_id %hWnd%
		InputBox, sNewTitle, Set Window Title,,,,,,,,, %sExistingTitle%

		g_iTimeAtLastExecution := SubStr(A_Now, StrLen(A_Now) - 3, 4)
		if (ErrorLevel)
			return

		WinSetTitle, ahk_id %hWnd%,, %sNewTitle%
	}

	return
}

#LButton::
{
	MouseGetPos,,, hWnd

	if (IsApprovedHwnd(hWnd))
	{
		sEnable := (DllCall("IsWindowEnabled", uint, hWnd) ? "Disable" : "Enable")
		WinSet, %sEnable%,, ahk_id %hWnd%
		TT_Out("Window " sEnable "d!")
	}

	return
}

!+C::
{
	MouseGetPos,,,, hCtrl, 2
	ControlGetText, sCtrlTxt,, ahk_id %hCtrl%
	ControlGetPos, iX, iY, iW, iH,, ahk_id %hCtrl%
	if !((iX == A_Blank || iY == A_Blank || iW == A_Blank || iH == A_Blank))
		clipboard := "Control:`t" sCtrlTxt "`nLeft:`t" iX "`nTop:`t" iY "`nRight:`t" iW "`nBottom:`t" iH
	return
}

TT_Out(sOutput)
{
	Tooltip, %sOutput%
	SetTimer, TT_Out, 2500
	return
}

TT_Out:
{
	Tooltip
	SetTimer, TT_Out, Off
	return
}

IsApprovedHwnd(hWnd)
{
	WinGetClass, sClass, ahk_id %hWnd%
	return !(sClass== "WorkerW"
				|| sClass == "Shell_TrayWnd"
				|| sClass== "Progman"
				|| sClass== "SideBar_HTMLHostWindow")
}

/*
===============================================================================
Function:   wp_GetMonitorAt (Modified by Verdlin to return monitor rect)
    Get the index of the monitor containing the specified x and y coordinates.

Parameters:
    x,y - Coordinates
    default - Default monitor
  
Returns:
   array of monitor coordinates

Author(s):
    Original - Lexikos - http://www.autohotkey.com/forum/topic21703.html
===============================================================================
*/
GetMonitorRectAt(x, y, default=1)
{
	SysGet, m, MonitorCount
	; Iterate through all monitors.
	Loop, %m%
	{ ; Check if the window is on this monitor.
		SysGet, Mon%A_Index%, MonitorWorkArea, %A_Index%
		if (x >= Mon%A_Index%Left && x <= Mon%A_Index%Right && y >= Mon%A_Index%Top && y <= Mon%A_Index%Bottom)
			return {left: Mon%A_Index%Left, right: Mon%A_Index%Right, top: Mon%A_Index%Top, bottom: Mon%A_Index%Bottom}
	}

	return {left: Mon%default%Left, right: Mon%default%Right, top: Mon%default%Top, bottom: Mon%default%Bottom}
}
1 个赞

如果您平时用的桌面环境是 MATE、Xfce、Cinnamon、GNOME 等基于 GTK+ 的桌面环境,或者窗口管理器,可以试一试 FSearch:

GUI 使用 GTK3 编写,界面和 Everything 很像,搜索速度也很快。

我之前试过一个专门的 alt 软件。但是没满足我的功能。后来我就没再折腾了。

如果 win 能够自带的话或许会好一些。

另外一个 windows 经常缺少的功能是 sshfs,也可以通过安装第三方软件解决。但是不如 linux 底下的好用。各种 linux 桌面一般也是自带。

Totalcmd mac上一直没有找到合适的

那当然是windows的微信,就是不支持深色模式。。。。。

windows上DesktopOK有这个功能,默认不开启,可以在菜单-工具-Alt+Drag功能里面开启

包管理器吧。离开了 Windows 然后再也没回去,一部分也是因为喜欢 Linux 发行版的包管理器设计。

Windows 上的一些平替:

  1. winget 管控过于中心化,普通用户添加包困难,经常因为一些啼笑皆非的理由删除或拒绝包
  2. scoop 目前来看综合最佳,但管控过于松散,很多国人的第三方源质量参差不齐,互相 copy 对方的粗制滥造的包文件又从不升级,各个巨型源常年霸占(污染)scoop 搜索结果;
  3. chocolatey 基本全面转向商业化,官网把社区版的下载页面都藏起来了,普通用户基本没法自行添加包,软件源数量还不如 winget。

现在用的是 Arch 的 pacman 包管理器,目前来看 AUR 上这种「用户编写包 + 集中管理包」的方式是最有利于生态发展的。

1 个赞

AltSnap 不用折腾,装上就用,因为其操作行为是复制自 Linux 并进行了部分扩展。

AutoHotkey 脚本也挺好,用大佬写的也不用折腾 :sunglasses:

windows有ahk,linux無

https://alternativeto.net/software/autohotkey/?platform=linux

对。就是这个 AltSnap,我装上了之后根本没有效果。我的操作系统是 Windows Server 2008 r2,不知道是不是服务器操作系统的原因。