2014-11-17 16 views
13

Tôi có một giai đoạn JavaFX chưa được đặt trước và mức tối thiểu của riêng tôi, tối đa hóa các nút đóng &. Nhưng tiếc là nhấp vào biểu tượng thanh tác vụ trong Windows 7 không tự động giảm thiểu giai đoạn - so với hành vi được trang trí.JavaFX tối thiểu hóa giai đoạn chưa được trang trí

Có cách nào để giảm thiểu giai đoạn chưa được trang trí bằng mã Java thuần túy không, bằng cách nhấp vào biểu tượng thanh tác vụ? Nếu không, làm thế nào tôi có thể làm điều này với, nói, JNA?

EDIT: OK, tôi đã cố gắng để giải quyết việc này với JNA, nhưng đã thực hiện bên cạnh không có C/C++/JNA, tôi có một rắc rối chút thiết lập này. Tôi muốn được biết ơn nếu ai đó giúp tôi để đưa các mảnh lại với nhau ..

Dưới đây là mã của tôi cho đến nay:

public final class Utils { 

    static { 
    if (PlatformUtil.isWin7OrLater()) { 
      Native.register("shell32"); 
      Native.register("user32"); 
     } 
    } 

    // Apparently, this is the event I am after 
    public static final int WM_ACTIVATEAPP = 0x1C; 


    public static void registerMinimizeHandler(Stage stage) { 
     // Hacky way to get a pointer to JavaFX Window 
     Pointer pointer = getWindowPointer(stage); 

     WinDef.HWND hwnd = new WinDef.HWND(pointer); 

     // Here's my minimize/activate handler 
     WinUser.WindowProc windowProc = new MinimizeHandler(stage); 

     Pointer magicPointer = ... set this to point to windowProc? 

     // This.. apparently, re-sets the WndProc? But how do I get the "magicPointer" that is "attached" to the windowProc? 
     User32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, magicPointer); 
    } 
} 

private static class MinimizeHandler implements WinUser.WindowProc { 

    private Stage stage; 

    private MinimizeHandler(Stage stage) { 
     this.stage = stage; 
    } 

    @Override 
    public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) { 
     if (uMsg == WM_ACTIVATEAPP) { 
      System.out.println("ACTIVATE"); 
     } 
     return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam); 
    } 
} 

private static Pointer getWindowPointer(Stage stage) { 
    try { 
     TKStage tkStage = stage.impl_getPeer(); 
     Method getPlatformWindow = tkStage.getClass().getDeclaredMethod("getPlatformWindow"); 
     getPlatformWindow.setAccessible(true); 
     Object platformWindow = getPlatformWindow.invoke(tkStage); 
     Method getNativeHandle = platformWindow.getClass().getMethod("getNativeHandle"); 
     getNativeHandle.setAccessible(true); 
     Object nativeHandle = getNativeHandle.invoke(platformWindow); 
     return new Pointer((Long) nativeHandle); 
    } catch (Throwable e) { 
     System.err.println("Error getting Window Pointer"); 
     return null; 
    } 
} 

EDIT 2: tôi cuối cùng đã thêm vào với thế này, nhưng trong thời gian sớm khi tôi thiết lập lại WNDPROC, cửa sổ chưa được trang trí của tôi không trả lời bất kỳ sự kiện nào. Tôi đang cung cấp một khoản tiền thưởng 100 danh tiếng cho một ví dụ độc lập với một giải pháp làm việc. Windows (7+) chỉ là OK, tôi thậm chí không biết làm thế nào điều này cư xử trên các nền tảng khác.

EDIT 3: Vâng, tôi loại bỏ với một này .. Tôi đã thiết lập mọi thứ một cách chính xác, và nhận được các sự kiện, nhưng có vấn đề tìm ra các sự kiện chính xác để lắng nghe ..

Vì có được một số lợi ích trong câu hỏi, nếu có ai muốn cố gắng để tiếp tục với điều này, đây là mã cuối cùng của tôi (nó hy vọng sẽ "làm việc" out-of-box):

public final class Utils { 

    static interface ExtUser32 extends StdCallLibrary, User32 { 
     ExtUser32 INSTANCE = (ExtUser32) Native.loadLibrary(
         "user32", 
         ExtUser32.class, 
         W32APIOptions.DEFAULT_OPTIONS); 

     WinDef.LRESULT CallWindowProcW(
         Pointer lpWndProc, 
         Pointer hWnd, 
         int msg, 
         WinDef.WPARAM wParam, 
         WinDef.LPARAM lParam); 

     int SetWindowLong(HWND hWnd, int nIndex, com.sun.jna.Callback wndProc) throws LastErrorException; 
    } 

    // Some possible event types 
    public static final int WM_ACTIVATE = 0x0006; 
    public static final int WM_ACTIVATEAPP = 0x1C; 
    public static final int WM_NCACTIVATE = 0x0086; 

    public static void registerMinimizeHandler(Stage stage) { 
     Pointer pointer = getWindowPointer(stage); 
     WinDef.HWND hwnd = new WinDef.HWND(pointer); 
     long old = ExtUser32.INSTANCE.GetWindowLong(hwnd, User32.GWL_WNDPROC); 
     MinimizeHandler handler = new MinimizeHandler(stage, old); 
     ExtUser32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, handler); 
    } 

    private static Pointer getWindowPointer(Stage stage) { 
    try { 
     TKStage tkStage = stage.impl_getPeer(); 
     Method getPlatformWindow = tkStage.getClass().getDeclaredMethod("getPlatformWindow"); 
     getPlatformWindow.setAccessible(true); 
     Object platformWindow = getPlatformWindow.invoke(tkStage); 
     Method getNativeHandle = platformWindow.getClass().getMethod("getNativeHandle"); 
     getNativeHandle.setAccessible(true); 
     Object nativeHandle = getNativeHandle.invoke(platformWindow); 
     return new Pointer((Long) nativeHandle); 
    } catch (Throwable e) { 
     System.err.println("Error getting Window Pointer"); 
     return null; 
    } 
} 

    private static class MinimizeHandler implements WinUser.WindowProc, StdCallLibrary.StdCallCallback { 

     private Pointer mPrevWndProc32; 

     private Stage stage; 

     private MinimizeHandler(Stage stage, long oldPtr) { 
      this.stage = stage; 

      mPrevWndProc32 = new Pointer(oldPtr); 

      // Set up an event pump to deliver messages for JavaFX to handle 
      Thread thread = new Thread() { 
       @Override 
       public void run() { 
        int result; 
        WinUser.MSG msg = new WinUser.MSG(); 
        while ((result = User32.INSTANCE.GetMessage(msg, null, 0, 0)) != 0) { 
         if (result == -1) { 
          System.err.println("error in get message"); 
          break; 
         } 
         else { 
          System.out.println("got message: " + result); 
          User32.INSTANCE.TranslateMessage(msg); 
          User32.INSTANCE.DispatchMessage(msg); 
         } 
        } 
       } 
      }; 
      thread.start(); 
     } 

     @Override 
     public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) { 

      if (uMsg == WM_ACTIVATEAPP) { 
       // Window deactivated (wParam == 0)... Here's where I got stuck and gave up, 
       // this is probably not the best event to listen to.. the app 
       // does indeed get iconified now by pressing the task-bar button, but it 
       // instantly restores afterwards.. 
       if (wParam.intValue() == 0) { 
        stage.setIconified(true); 
       } 
       return new WinDef.LRESULT(0); 
      } 

      // Let JavaFX handle other events 
      return ExtUser32.INSTANCE.CallWindowProcW(
          mPrevWndProc32, 
          hWnd.getPointer(), 
          uMsg, 
          wParam, 
          lParam); 
     } 
    } 
} 
+0

Có thể bạn cần khởi động máy bơm sự kiện. Nhìn vào vòng lặp 'GetMessage()' trong [mã ví dụ này] (https://github.com/twall/jna/blob/master/contrib/w32keyhook/com/sun/jna/contrib/demo/KeyHook.java) . Khi mã Java của bạn đang chạy máy bơm sự kiện, cửa sổ và móc sự kiện của bạn sẽ bắt đầu nhận tin nhắn đúng cách. – technomage

+0

Bạn cũng có thể tận dụng 'Native.getWindowHandle()' của JNA để có được cửa sổ gốc xử lý, thay vì tra cứu dựa trên sự phản chiếu mà bạn đang sử dụng. Tùy thuộc vào cách các công cụ JavaFX xử lý các đồng nghiệp bản địa của nó, mặc dù. – technomage

+0

@technomage Cảm ơn nhận xét của bạn. Tôi tạo ra một Chủ đề mới và bắt đầu bơm sự kiện này trong đó, và cửa sổ giờ đây thực sự đáp ứng với các sự kiện! Tuy nhiên, khi tôi nhấp vào biểu tượng thanh tác vụ của ứng dụng của tôi, CallWindowProcW không bao giờ được gọi. Nhưng, nó được gọi trên nhiều sự kiện khác: ví dụ: khi tôi tối đa hóa ứng dụng từ nút tối đa của riêng mình, tôi sẽ nhận được tin nhắn được in qua một cuộc gọi lại. Làm cách nào để tôi thực sự nắm bắt "sự kiện nhấp vào thanh tác vụ"? – user2499946

Trả lời

7

Bạn chỉ có thể đặt kiểu cửa sổ thích hợp. Nó hoạt động trong XP nhưng nên được ok trong windows 7 32 bit. Tôi nghĩ (nhưng không thể kiểm tra) nếu bạn sử dụng 64 bit, sau đó thay đổi các chức năng của cửa sổ Ptr, tức là. GetWindowLongPtr.

import com.sun.jna.Native; 
import com.sun.jna.Pointer; 
import com.sun.jna.platform.win32.User32; 
import com.sun.jna.platform.win32.WinDef.HWND; 
import com.sun.jna.platform.win32.WinUser; 
import static com.sun.jna.platform.win32.WinUser.GWL_STYLE; 
import javafx.application.Application; 
import javafx.scene.Scene; 
import javafx.scene.control.TextArea; 
import javafx.scene.layout.VBox; 
import javafx.stage.Stage; 

public class JNATest extends Application { 
    public static void main(String[] args) { launch(args); } 

    @Override 
    public void start(Stage stage) { 
     TextArea ta = new TextArea("output\n"); 
     VBox root = new VBox(5,ta); 
     Scene scene = new Scene(root,800,200); 
     stage.setTitle("Find this window"); 
     stage.setScene(scene); 
     stage.show(); 
     //gets this window (stage) 
     long lhwnd = com.sun.glass.ui.Window.getWindows().get(0).getNativeWindow(); 
     Pointer lpVoid = new Pointer(lhwnd); 
     //gets the foreground (focused) window 
     final User32 user32 = User32.INSTANCE; 
     char[] windowText = new char[512]; 
     HWND hwnd = user32.GetForegroundWindow(); 
     //see what the title is 
     user32.GetWindowText(hwnd, windowText, 512); 
     //user32.GetWindowText(new HWND(lpVoid), windowText, 512);//to use the hwnd from stage 
     String text=(Native.toString(windowText)); 
     //see if it's the same pointer 
     ta.appendText("HWND java:" + lpVoid + " HWND user32:"+hwnd+" text:"+text+"\n"); 
     //change the window style if it's the right title 
     if (text.equals(stage.getTitle())){ 
      //the style to change 
      int WS_DLGFRAME = 0x00400000;//s/b long I think 
      //not the same constant here?? 
      ta.appendText("windows api:"+WS_DLGFRAME+" JNA: "+WinUser.SM_CXDLGFRAME); 
      int oldStyle = user32.GetWindowLong(hwnd, GWL_STYLE); 
      int newStyle = oldStyle & ~0x00400000; //bitwise not WS_DLGFRAME means remove the style 
      newStyle = newStyle & ~0x00040000;//WS_THICKFRAME 
      user32.SetWindowLong(hwnd, GWL_STYLE, newStyle); 
     } 
    } 

} 

tôi đoán là bạn thay thế 3 dòng cuối cùng với

  long oldStyleLong = user32.GetWindowLongPtr(hwnd, GWL_STYLE).longValue(); 
      long newStyleLong = oldStyleLong & ~ 0x00400000l; 
      user32.SetWindowLongPtr(hwnd, GWL_STYLE, new BaseTSD.LONG_PTR(newStyleLong)); 

cho 64 bit. Tôi nghĩ rằng tôi không có những chức năng trong User32.dll của tôi, vì vậy tôi không thể kiểm tra nó. Có rất nhiều mã không liên quan trong đó, chủ yếu là để thử nghiệm hoặc giảng dạy. Hủy bỏ các dòng không sử dụng một khi bạn tìm ra những gì bạn muốn làm.

ps. Không thêm newStyle = newStyle & ~0x00020000;//WS_MINIMIZEBOX. Đó là một trong những cờ kiểu JavaFX không sử dụng cho undecorated. Đó là lý do tại sao giảm thiểu không có sẵn. Có thể nếu bạn thử đặt giai đoạn chưa được trang trí và thêm (sử dụng |, không phải là & ~) cờ hộp thu nhỏ, bạn sẽ nhận được kết quả tương tự. Có các công cụ để tìm kiếm tất cả các cờ kiểu từ bất kỳ cửa sổ nào.

Đây là số lượng mã đơn giản nhất mà chỉ thay đổi một giai đoạn chưa được trang trí bằng cách sử dụng HWND của giai đoạn.

public void start(Stage stage) { 
     Scene scene = new Scene(new Pane(new Label("Hello World"))); 
     stage.initStyle(StageStyle.UNDECORATED); 
     stage.setTitle("Find this window"); 
     stage.setScene(scene); 
     stage.show(); 
     long lhwnd = com.sun.glass.ui.Window.getWindows().get(0).getNativeWindow(); 
     Pointer lpVoid = new Pointer(lhwnd); 
     HWND hwnd = new HWND(lpVoid); 
     final User32 user32 = User32.INSTANCE; 
     int oldStyle = user32.GetWindowLong(hwnd, GWL_STYLE); 
     System.out.println(Integer.toBinaryString(oldStyle)); 
     int newStyle = oldStyle | 0x00020000;//WS_MINIMIZEBOX 
     System.out.println(Integer.toBinaryString(newStyle)); 
     user32.SetWindowLong(hwnd, GWL_STYLE, newStyle); 
    } 

Nó sẽ in các cờ kiểu trước và sau để bạn có thể tìm kiếm kiểu nào được đặt.

+0

Đây là một câu trả lời hay, nhưng không phải là _exactly_ những gì tôi theo sau, ít nhất là ở dạng hiện tại của nó. cửa sổ (thanh tiêu đề đã biến mất ngay bây giờ), và lý tưởng, tôi đã hy vọng để có được thoát khỏi trang trí bản địa hoàn toàn.Bằng cách này, phương pháp này dường như làm việc tốt trên Win7 64bit của tôi, tuy nhiên đề nghị sửa chữa 3 dòng của bạn không hoạt động (Tôi thực sự bị mất thanh tiêu đề từ cửa sổ Sublime Text trong khi rối tung với những thứ này :). – user2499946

+0

Nếu văn bản cao cấp được tập trung (có thể luôn ở trên cùng) thì nó sẽ mất thanh tiêu đề. Đó là lý do tại sao tôi cung cấp 2 phương pháp để có được hwnd. Một từ api mặt trời riêng và một từ user32. Bạn cũng có thể loại bỏ đường viền (tôi sẽ cập nhật). Bạn có thể kiểm tra tất cả các [kiểu cửa sổ cơ bản tại đây.] (Http://msdn.microsoft.com/en-us/library/windows/desktop/ms632600 (v = vs.85) .aspx) – brian

+0

Tôi vừa nhớ là tôi đã thêm kiểm tra để đảm bảo HWND trỏ đến cửa sổ có cùng tiêu đề. Nó không nên lộn xộn bất kỳ cửa sổ nào khác. – brian

-2

đã không nhận được của bạn câu hỏi đúng ... nhưng đây là giải pháp

@FXML private void minimize() 
{ 
Stage stage = (Stage) minimize.getScene().getWindow(); 
stage.setIconified(true); 
} 
Các vấn đề liên quan