• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            逛奔的蝸牛

            我不聰明,但我會(huì)很努力

               ::  :: 新隨筆 ::  ::  :: 管理 ::
            @import url(http://m.shnenglu.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css); 首先大家看Apple關(guān)于NSButton的描述,NSButton跟NSWindow一樣,它的外觀形式也是委托給NSButtonCell來處理的,自身只包含邏輯代碼。
            所以重繪NSButton就是重繪NSButtonCell啦,然后把NSButton的cell設(shè)置位你自己的cell就好了。

            1)重繪目標(biāo)
            首先觀察一下系統(tǒng)NSButton的行為和外觀表現(xiàn),可以發(fā)現(xiàn)默認(rèn)Button(快捷健設(shè)置為return)是有一個(gè)一閃一閃的效果,鼠標(biāo)點(diǎn)擊其他非默認(rèn)button的時(shí)候同window上默認(rèn)button的藍(lán)色消失,同時(shí)被點(diǎn)中button變成藍(lán)色。放開鼠標(biāo),默認(rèn)button恢復(fù)藍(lán)色背景并閃爍,被點(diǎn)擊button變白色。
            重繪一個(gè)控件最好是不要改變其默認(rèn)行為,也最好不要違反Apple的關(guān)于界面設(shè)計(jì)的建議文檔。所以我們的目標(biāo)是重繪出來的button是灰色漸變背景,默認(rèn)button有一個(gè)黃色的圈圈圍在周圍,不閃爍。被點(diǎn)中的button顯示黃色圈圈,默認(rèn)button黃色圈圈消失。
            效果如下圖:
            鼠標(biāo)未按下效果

             
            鼠標(biāo)按下效果

             

            2)漸變背景
            NSButtonCell的重繪方法很簡(jiǎn)單,重寫下面的方法即可。
            邏輯就是
            1)檢測(cè)當(dāng)前button的類型(普通button,checkbox,radiobutton等)
            2)畫button的基本形狀和顏色
            3)如果當(dāng)前button被click了,那么顯然的畫一個(gè)黃色的圈圈上去
            4)如果沒有被click,那么檢測(cè)是否為默認(rèn)button,如果是,并且當(dāng)前window沒有被click的其他button,那么為自己畫一個(gè)黃色的圈圈,否則不畫。

            // buttoncell有一個(gè)私有方法來標(biāo)示當(dāng)前button的類型
            // 這里只列出關(guān)心的三種類型

            typedef enum KAButtonType{
                KACheckBox = 3,
                KARadioButton = 4,
                KARoundButton = 7
            };

            - (void)drawWithFrame: (NSRect)cellFrame inView: (NSView *)controlView
            {
            switch ([self _buttonType]) {
             // buttonCell的私有函數(shù),可以確定button類型,10.4/10.5/10.6都可用
                        case KACheckBox:
                            [self drawCheckInFrame:cellFrame isRadio:NO]; 
            // 畫checkbox的形狀,這里忽略不畫
                            break;
                        case KARadioButton:
                            [self drawCheckInFrame:cellFrame isRadio:YES];
             // 畫radiobutton的形狀,這里忽略不畫
                            break;
                        default:
                            switch ([buttonCell bezelStyle]) {
             // 這就是button啦,默認(rèn)的形狀,這個(gè)參數(shù)可以在IB里設(shè)置,
                                                                                       // 所以button的類型必須為NSRoundedBezelStyle,當(dāng)然你可以改為其他的
                                case NSRoundedBezelStyle:
                                    [self drawRoundedButtonInFrame: cellFrame inView: controlView];
                                    break;
                                    
                         case NSRegularSquareBezelStyle:
                                    [self drawHyperLinkButtonInFrame: cellFrame];
                                    break;
                                default:
                                    break;
                            }
                            break;
                    }

                    
                    // 畫Button的圖片哦
                    // Comment by yoyokko
                    // if [buttonCell _normalImage] is nil, that to say there is a missing 
                    // field in nib file for this check box --> 
                    // NSButtonCell uses function <(int)_buttonType> to determine button type.
                    // After hacking, I found that 3==Checkbox, 4==Radio, 7==RoundedButton

                    if([buttonCell _buttonType] == KARoundButton)
                    {    
                        if([buttonCell imagePosition] != NSNoImage) {
                            [self drawImage: [buttonCell image] withFrame: cellFrame inView: [buttonCell controlView]];
                        }
                    }
            }

            // 查詢當(dāng)前window上有沒有被click的button
            - (void)travelSubViews: (NSView*)view
            {
                NSArray *items = [view subviews];
                NSEnumerator *enumerator = [items objectEnumerator];
                id anObject = nil;
                while (anObject = [enumerator nextObject]) 
                {
                    if ([anObject isKindOfClass: [NSButton class]])
                    {
                        NSButtonCell *buttonCell = [anObject cell];
                        NSBezelStyle buttonStyle = [buttonCell bezelStyle];
                        if ([buttonCell isHighlighted] &&
                            (buttonStyle == NSRoundedBezelStyle || buttonStyle == NSTexturedRoundedBezelStyle))
                        {
                            [self setMIsFound: YES];
                            break;
                        }
                    }
                    else
                    {
                        [self travelSubViews: anObject];
                    }
                }    
            }


            // 畫漸變的button和黃色圈圈
            -(void)drawRoundedButtonInFrame:(NSRect)frame inView: (NSView *)controlView
            {    
                NSRect textFrame;
                
                //Adjust Rect so strokes are true and
                //shadows are visible
                frame.origin.x += .5f;
                frame.origin.y += .5f;
                frame.size.height -= 1;
                frame.size.width -= 1;
                
                //Adjust Rect based on ControlSize so that
                //my controls match as closely to apples
                //as possible.
                switch ([buttonCell controlSize]) {
                    default: // Silence uninitialized variable warnings for textFrame fields.
                    case NSRegularControlSize:
                        
                        frame.origin.x += 4;
                        frame.origin.y += 4;
                        frame.size.width -= 8;
                        frame.size.height -= 12;
                        
                        textFrame = frame;
                        break;
                        
                    case NSSmallControlSize:
                        
                        frame.origin.x += 4;
                        frame.origin.y += 4;
                        frame.size.width -= 8;
                        frame.size.height -= 11;
                        
                        textFrame = frame;
                        textFrame.origin.y += 1;
                        break;
                        
                    case NSMiniControlSize:
                        
                        frame.origin.y -= 1;
                        
                        textFrame = frame;
                        textFrame.origin.y += 1;
                        break;
                }
                
                //Create Path
                NSBezierPath *path = [[NSBezierPath alloc] init];
                [path appendBezierPathWithRoundedRect: frame cornerRadius:6.0f];
                if([buttonCell isEnabled]) 
                {    
                    // draw inner part of button first

                            // 畫button的灰色漸變部分
                    [self drawShadingWithStartingColor: [self colorVlaueWithRed: 239 green: 239 blue: 239]//[NSColor blackColor]
                                       withEndingColor: [self colorVlaueWithRed: 93 green: 93 blue: 93]//[NSColor whiteColor]
                                          inBezierPath: path];

                    
                    // draw focus ring second
                   // 當(dāng)當(dāng)前button被click時(shí),畫那個(gè)黃色的圈圈
                    // if the button is highlighted, then draw a ring around the button
                    if([buttonCell isHighlighted]) // 當(dāng)button被click時(shí),isHighlighted返回YES
                    {            
                        [[self colorVlaueWithRed: 246 green: 186 blue: 55] set];
                        [path setLineWidth: 3.0f];
                        [path stroke];        
                    } 
                    else
                    {

                      // button沒有被click,那就檢查是否為默認(rèn)的button
                        // otherwise, check if it is a default button
                        id btnControl = [buttonCell controlView];
                    
                        if ([btnControl respondsToSelector: @selector(keyEquivalent)] && [[btnControl keyEquivalent] isEqualToString: @"\r"])
                        { 

                            // 如果是默認(rèn)button
                            NSView *superView = controlView;
                            NSView *tempView = nil;
                            for (tempView = superView; tempView != nil; tempView = [tempView superview])
                                superView = tempView;

                            // 找到當(dāng)前window的contentview
                            if (superView)
                            {
                                [buttonCell setMIsFound:NO];
                                [buttonCell travelSubViews: superView];
                            }
                            

                            // 看當(dāng)前window中有沒有被click的button,沒有就把自己這個(gè)默認(rèn)button畫一個(gè)黃圈
                            if (![buttonCell mIsFound])
                            {
                                [[self colorVlaueWithRed: 246 green: 186 blue: 55] set];
                                [path setLineWidth: 3.0f];
                                [path stroke];
                            }
                            
                            [buttonCell setMIsFound:NO];
                        }
                    }
                    
                } 
                else 
                {        

                    // button 沒有enable
                    [self drawShadingWithStartingColor: [self colorVlaueWithRed: 220 green: 220 blue: 220]//[NSColor blackColor]
                                       withEndingColor: [self colorVlaueWithRed: 112 green: 112 blue: 112]//[NSColor whiteColor]
                                          inBezierPath: path];
                }
                
                [path release];

                
                    // 畫button的text,這里忽略不畫
                if([buttonCell imagePosition] != NSImageOnly) {        
                    [self drawTitle: [buttonCell attributedTitle] withFrame: textFrame inView: [buttonCell controlView]];
                }
            }



            至此,所有繪制的代碼工作都已經(jīng)完成了,包括黃色圈圈和點(diǎn)擊其他button的行為都寫好了~
            但這樣做會(huì)有一個(gè)問題……


            3)更改系統(tǒng)默認(rèn)畫黃色圈圈的行為
            釋下面一段代碼的行為,這個(gè)很重要,否則會(huì)出現(xiàn)非常巧妙的bug……很奇妙,困擾了我兩個(gè)星期的bug,恨哪~

            - (void)heartBeat:(CDAnonymousStruct7 *)fp8
            {
                id btnControl = [self controlView];

                if ([btnControl respondsToSelector: @selector(keyEquivalent)] && [[btnControl keyEquivalent] isEqualToString: @"\r"])// && !oneButtonClicked)
                {
                    [btnControl setNeedsDisplay:YES];
                }
            }


            首先探索一下系統(tǒng)默認(rèn)button的一閃一閃的行為是怎么做的,blabla一大堆,經(jīng)過hack發(fā)現(xiàn),每個(gè)程序在起來之后都會(huì)啟動(dòng)一個(gè)叫做HeartBeat的線程。每個(gè)control都有一個(gè)heartBeat:的函數(shù)。
            這個(gè)線程負(fù)責(zé)默認(rèn)button的一閃一閃的刷新,spin的旋轉(zhuǎn)等,所以在你的主界面block住的時(shí)候你會(huì)發(fā)現(xiàn)button還在閃,spin還在轉(zhuǎn),而你自己用timer寫的progressspin是不會(huì)轉(zhuǎn)的。對(duì)于一個(gè)window來說,它上面的button不會(huì)一直刷新,只是顯示的時(shí)候刷幾次,而默認(rèn)button會(huì)被heartbeat線程調(diào)用一直刷新。

            問題就出在這里,這是一個(gè)線程啊,我們重寫了buttoncell的繪制函數(shù),但我們并沒有做處理并保證這個(gè)函數(shù)是原子的調(diào)用啊,所以這里會(huì)發(fā)生非常極品的問題(當(dāng)用多線程繪制界面時(shí)一定要注意是原子操作)
            首先有一個(gè)程序彈出了一個(gè)sheet,然后這個(gè)sheet上有一個(gè)button,點(diǎn)擊button會(huì)再次彈出一個(gè)sheet,不知道是不是apple的這里的消息循環(huán)有問題,在點(diǎn)擊這個(gè)button彈出sheet的同時(shí),button所在的window或者新彈出的window上有的button會(huì)被刷成別的形狀,比如某個(gè)radiobutton的字變成了OK,或者就變成了一個(gè)拉長(zhǎng)版的普通button,并且只會(huì)變成默認(rèn)button的字或者形狀。
            這就是因?yàn)槎嗑€程的原因造成的。在刷當(dāng)前button的時(shí)候,heartbeat來搗亂了,不知道怎么搞得就把默認(rèn)button的字或者形狀刷到了當(dāng)前button的信息上面(button的text就是被改變了)。不太清楚默認(rèn)的heartBeat:里面做了些什么。
            所以這里只能重寫heartBeat:函數(shù)(亦或把重繪函數(shù)變成原子的,沒試過),在這個(gè)函數(shù)里面啥都不做,只是檢測(cè)當(dāng)前button是否為默認(rèn)button,是的畫就通知主線程來刷新。
            因?yàn)檫@里只是加一個(gè)黃色圈圈而已,所以即使主線程block住也沒什么問題。

            JB,非常JB~

            PS:在10.4上程序起來時(shí)heartbeat線程不能正常起來,所以需要在程序結(jié)束launching之后談一個(gè)sheet,再把之關(guān)閉就可以了(很奇怪,估計(jì)Tiger上的消息循環(huán)還是有很大的問題的)。
            @import url(http://m.shnenglu.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);

            From: http://www.cocoachina.com/bbs/read.php?tid=14590
            posted on 2011-12-09 04:00 逛奔的蝸牛 閱讀(2949) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Cocoa
            久久久WWW成人免费精品| 精品国际久久久久999波多野| 99久久国产综合精品成人影院 | 久久这里有精品视频| 亚洲精品久久久www| 久久天天躁狠狠躁夜夜avapp| 精品国产一区二区三区久久| 久久精品亚洲精品国产欧美| 狠狠色丁香久久婷婷综合| 亚洲国产精品人久久| 久久强奷乱码老熟女网站| 久久99国产精品一区二区| 欧美日韩久久中文字幕| 韩国三级大全久久网站| 亚洲精品综合久久| 狠狠色噜噜狠狠狠狠狠色综合久久 | 日日狠狠久久偷偷色综合96蜜桃 | 久久婷婷五月综合97色直播| 久久精品亚洲精品国产色婷| 久久九九久精品国产免费直播| 久久天天躁狠狠躁夜夜躁2O2O| 久久久久久亚洲精品影院| 色综合久久综精品| 精品久久久久久国产潘金莲| 午夜欧美精品久久久久久久| 伊人久久无码精品中文字幕| 国产精品99久久精品爆乳| 国产成人无码久久久精品一| 久久精品国产日本波多野结衣| 色欲综合久久躁天天躁| 日韩亚洲欧美久久久www综合网| 色欲av伊人久久大香线蕉影院| 少妇人妻综合久久中文字幕| 日韩电影久久久被窝网| 久久久精品日本一区二区三区| 伊人丁香狠狠色综合久久| 成人精品一区二区久久| 国产高潮国产高潮久久久91 | 97久久精品无码一区二区| 久久精品国产亚洲AV高清热| 久久Av无码精品人妻系列|